Como posso grep um diretório para linhas que contenham "Foo", mas só recebo correspondências quando a próxima linha também contém "Bar"?
Como posso grep um diretório para linhas que contenham "Foo", mas só recebo correspondências quando a próxima linha também contém "Bar"?
Respostas:
@ 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' .
O próprio Grep não parece suportá-lo, use o pcregrep:
Foo
Bar
Foo
abc
pcregrep -M "Foo\nBar" file
Obteve:
Foo
Bar
Foo
e Bar
compreenderia toda a linha.
Com um sed
script:
#!/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-o para sed
.
Nota : isso é classificado por ordem alfabética.
Mais informações úteis pattern space
e hold space
AQUI .
O grymoire.com tem coisas realmente boas sobre shell
programação.
h, n, H, x, p, q
significa? Muito interessante.
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
Usando grep
apenas, você pode construir o seguinte canal:
grep -A1 'Foo' input_file | grep -B1 'Bar' | grep 'Foo'
O primeiro grep
receberá todas as linhas que contêm Foo
, bem como a linha após a partida. Em seguida, obtemos as linhas que contêm Bar
e 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 à grep
funcionalidade orientada à linha, qualquer solução com ela provavelmente será um "hack" e você provavelmente estará melhor usando algo como o pcregrep
que é mais adequado para a tarefa em questão.
find . -name '*.txt' | xargs grep -A1 'Foo' | grep -B1 'Bar'
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:
-o
imprimir apenas parte correspondente. Necessário, pois a inclusão de -z
imprimirá 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
-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.
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 ./filename
vez de filename
para o awk)