Como grep bloqueia? Ou eu deveria usar o awk / ack?

6

Digamos que eu tenha um arquivo com informações da seguinte forma:

...
Entry '234238': some text
  some text
  some text
  some text
Entry '899823': some text
  some text
  some text
Entry '234238': more text
  more text
  more text
Entry '645353': some text
  some text
  some text

Gostaria de extrair um Entry '<code>' específico. Por exemplo, grep_my_block 'Entry '234238' deve retornar:

Entry '234238': some text
  some text
  some text
  some text
Entry '234238': more text
  more text
  more text

Observe que:

  1. O <code> que identifica um bloco pode aparecer várias vezes no arquivo. Queremos extrair todos esses blocos.
  2. Blocos podem consistir em um número desconhecido de linhas

Como faço isso com grep , awk ou ack ?

    
por Amelio Vazquez-Reina 31.03.2014 / 00:46

4 respostas

6
awk "/^Entry '234238'/ {printline = 1; print; next}
     /^Entry / {printline = 0}
     printline"
    
por 31.03.2014 / 00:59
3
ENTRY="'234238'"
sed -n ':s;/Entry '"$ENTRY"'/{:l;p;n;/^Entry/bs;bl;}' <<\ENTRY
    Entry '234238': some text
        some text
        some text
        some text
    Entry '899823': some text
        some text
        some text
    Entry '234238': more text
        more text
        more text
    Entry '645353': some text
        some text
        some text
#END
ENTRY

OUTPUT

Entry '234238': some text
    some text
    some text
    some text
Entry '234238': more text
    more text
    more text

Isso deve ser muito mais rápido do que awk (acho) devido a sed's operações de fluxo.

Esse foi um dos menos complicados que fiz - uma vez que envolvi minha cabeça em torno dele. Este é um dos primeiros que consegui fazer sem requerer o regex estendido do GNU - isso deve ser bastante portátil.

Isso se ramifica duas vezes - há um achor :s no início e uma âncora :l para o subscrito. Isso funciona porque o operador n exclui a linha anterior de sed's pattern-space quando puxa uma nova.

Uma vez que sed encontre o seu "$ENTRY" , ele define o ramo :l abel, imprime a linha e extrai um novo. Em seguida, sed verifica se a nova linha começa com a frase 'Entry' . Nesse caso, ela será ramificada para :s tart rotular e começar a escanear sua entrada novamente para o seu "$ENTRY," senão ela só será ramificada até :l abel e repetirá o p rint, n ext, /check/ operação.

Este comando resume-se ao seguinte:

until end of file do
    if current line contains "Entry $ENTRY" do
        until next line contains 'Entry' do
            print line
            delete line 
            next line
        done
    done
done
    
por 31.03.2014 / 04:46
1

Você também pode usar pcregrep :

pcregrep -M '234238.*(\n((?!Entry).)*)*' inputfile

Isso produziria todas as linhas, começando da que continha 234238 até encontrar uma que contenha a palavra Entry .

Para sua entrada de amostra, ela produz:

Entry '234238': some text
  some text
  some text
  some text
Entry '234238': more text
  more text
  more text
    
por 31.03.2014 / 07:34
1

awk é provavelmente uma boa ferramenta para usar, já que o problema é orientado à linha.

Eu usaria essa variante da solução @ HaukeLaging, que tem menos redundância no código. Cada linha que começa com Entry limpa um sinalizador, mas o cabeçalho da entrada específica que você deseja define o sinalizador. Se o sinalizador estiver definido, execute a ação padrão de imprimir a linha.

awk "/^Entry /         { printline=0; }
     /^Entry '234238'/ { printline=1; }
     printline"
    
por 31.03.2014 / 11:19