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

11

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

Nathan Long
fonte
O problema agora é totalmente diferente do original: / Talvez seja melhor reverter as versões antigas e POST outra? Além disso, a nova pergunta não está clara para mim.
Gilles Quenot
@sputnick - como assim? Especifiquei um diretório quando publiquei a pergunta; Eu apenas o atrevi porque as pessoas não estavam percebendo.
Nathan Long
Deixa pra lá, isso vai funcionar, vou editar meu POST de acordo.
Gilles Quenot

Respostas:

7

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

Isso parece funcionar para mim.

pcregrep -rMi 'Foo(.*)\n(.*)Bar' .
Nathan Long
fonte
6

O próprio Grep não parece suportá-lo, use o pcregrep:

Foo
Bar
Foo
abc

pcregrep -M "Foo\nBar" file

Obteve:

Foo
Bar
margarida
fonte
3
O OP não disse isso Fooe Barcompreenderia toda a linha.
amigos estão dizendo sobre tojrobinson
6

Com um sedscript:

#!/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 printfaqui exibe todos os arquivos no diretório atual em uma linha cada e passa-o para sed.

Nota : isso é classificado por ordem alfabética.

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

O grymoire.com tem coisas realmente boas sobre shellprogramação.

Gilles Quenot
fonte
O que h, n, H, x, p, qsignifica? Muito interessante.
Yamaneko
Veja meus comentários. Mais informações em pattern space& hold space: grymoire.com/Unix/Sed.html#uh-56 ou em francês commentcamarche.net/faq/9536-sed-introduction-a-sed-part-i
Gilles Quenot
POST adaptado para trabalhar em um diretório
Gilles Quenot
4

Usando grepapenas, você pode construir o seguinte canal:

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

O primeiro grepreceberá todas as linhas que contêm Foo, bem como a linha após a partida. Em seguida, obtemos as linhas que contêm Bare a linha antes da partida e, finalmente, extraímos as linhas dessa saída que contém Foo.

EDIT: Como apontou o manatwork , há alguns casos problemáticos a serem observados. Embora seja um desafio interessante, devido à grepfuncionalidade orientada à linha, qualquer solução com ela provavelmente será um "hack" e você provavelmente estará melhor usando algo como o pcregrepque é mais adequado para a tarefa em questão.

tojrobinson
fonte
Agradável. Eu perguntei sobre um diretório; isso parece funcionar:find . -name '*.txt' | xargs grep -A1 'Foo' | grep -B1 'Bar'
Nathan Long
Isso também listará ocorrências com "Foo" e "Bar" na mesma linha.
manatwork
@manatwork: Linhas que contêm "Foo" e "Bar" são "linhas que contêm 'Foo'", e foi isso que foi solicitado.
perfil completo de tojrobinson
1
@tojrobinson, o que dizer da parte “mas recebe correspondências quando a próxima linha também contém a parte" Bar ""? pastebin.com/Yj8aeCEA
manatwork
3

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

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

Explicação das opções:

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

EDIT: Esta versão imprime linhas completas correspondentes

    grep -o -P -z  '(.*)Foo(.*)\n(.*)Bar(.*)' file
bbaja42
fonte
1
Truque legal o que -z. Alguns "(. *)" Antes e depois de toda a expressão produziriam todas as linhas correspondentes. Por enquanto, substrings antes de "Foo" e depois de "Bar" não são exibidos.
manatwork
1

Com awk:

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

(observação geral sobre a limitação do awk: lembre-se de que, se alguns nomes de arquivos contiverem caracteres "=", será necessário passá-los como em ./filenamevez de filenamepara o awk)

Stéphane Chazelas
fonte