Como posso grep um diretório baseado no conteúdo de duas linhas sucessivas?

11

Como posso grep um diretório para linhas que contêm "Foo", mas somente obtém correspondências quando a próxima linha também contém "Barra"?

    
por Nathan Long 08.10.2012 / 17:14

6 respostas

7

@ warl0ck me apontou na direção certa com pcregrep , mas eu disse "contém", não "é", e eu perguntei sobre um diretório, não um arquivo.

Isso parece funcionar para mim.

pcregrep -rMi 'Foo(.*)\n(.*)Bar' .
    
por 08.10.2012 / 17:59
6

O próprio Grep parece não suportar, use o pcregrep em vez disso:

Foo
Bar
Foo
abc

pcregrep -M "Foo\nBar" file

Tem:

Foo
Bar
    
por 08.10.2012 / 17:26
6

Com um script sed :

#!/bin/sed -nf

/^Foo/{
    h         # put the matching line in the hold buffer
    n         # going to nextline
    /^Bar/{   # matching pattern in newline
        H     # add the line to the hold buffer
        x     # return the entire paragraph into the pattern space
        p     # print the pattern space
        q     # quit the script now
    }
}

Para usá-lo:

chmod +x script.sed
printf '%s\n' * | ./script.sed

O printf aqui exibe todos os arquivos no diretório atual em uma linha cada, e passa para sed .

Nota : esta é ordenada por ordem alfabética.

Mais informações sobre pattern space e hold space úteis AQUI .

grymoire.com tem coisas realmente boas sobre shell programação.

    
por 08.10.2012 / 18:39
4

Usando apenas grep , você pode construir o seguinte canal:

grep -A1 'Foo' input_file | grep -B1 'Bar' | grep 'Foo'

O primeiro grep obterá todas as linhas que contêm Foo , bem como a linha após a correspondência. Em seguida, obtemos linhas que contêm Bar , bem como a linha antes da correspondência e, finalmente, extrair as linhas dessa saída que contêm Foo .

EDITAR: Como manatwork apontou, existem alguns casos problemáticos a serem observador de. Apesar de ser um desafio interessante, devido à funcionalidade orientada a linha de grep , qualquer solução com ele provavelmente é um 'hack' e você provavelmente está melhor usando algo como pcregrep que é mais adequado para a tarefa em questão.

    
por 08.10.2012 / 17:56
3

Embora eu prefira a solução de Nathan usando pcregrep , aqui está a solução usando apenas o grep

grep -o -z -P  'Foo(.*)\n(.*)Bar' file

Explicação das opções:

  • -o imprime apenas a parte correspondente. Necessário já que a inclusão de -z imprimirá todo o arquivo (a menos que haja um \ 0 em algum lugar)
  • -z Trate a entrada como um conjunto de linhas, cada uma terminada por um zero           byte (o caractere ASCII NUL) em vez de uma nova linha.
  • -P perl sintaxe regex

EDIT: Esta versão imprime linhas inteiras combinadas

    grep -o -P -z  '(.*)Foo(.*)\n(.*)Bar(.*)' file
    
por 09.10.2012 / 10:12
1

com o awk:

awk '/bar/ && prev != "" {print FILENAME ": " prev "\n" FILENAME ": " $0}
     /foo/ {prev=$0; next}
     {prev=""}' file1...

(nota geral sobre a limitação do awk: cuidado para que, se alguns nomes de arquivos contiverem caracteres "=", você precisará passá-los como ./filename em vez de filename para awk)

    
por 08.10.2012 / 22:29