Como posso procurar um padrão de múltiplas linhas em um arquivo?


128

Eu precisava encontrar todos os arquivos que continham um padrão de string específico. A primeira solução que vem à mente é usar o find piped com xargs grep :

find . -iname '*.py' | xargs grep -e 'YOUR_PATTERN'

Mas se eu precisar encontrar padrões que se estendam em mais de uma linha, eu estou preso porque o vanilla grep não pode encontrar padrões de várias linhas.



2
Essa é mais velho, então eu diria que não é uma duplicata :)
rogerdpack

@rogerdpack Ao marcar perguntas como duplicadas, a idade de uma pergunta é uma preocupação terciária, após a quantidade e a qualidade das respostas e a qualidade da pergunta.
Tripleee 13/07/19

Respostas:


98

Então eu descobri o pcregrep, que significa Expressões regulares compatíveis com Perl GREP .

Por exemplo, você precisa encontrar arquivos onde a variável ' _name ' é imediatamente seguida pela variável ' _description ':

find . -iname '*.py' | xargs pcregrep -M '_name.*\n.*_description'

Dica: você precisa incluir o caractere de quebra de linha no seu padrão. Dependendo da sua plataforma, pode ser '\ n', \ r ',' \ r \ n ', ...


7
Conforme mencionado por halka abaixo, "você também pode convencer o caractere curinga de pontos a corresponder às novas linhas se adicionar (? S) à sua expressão regular". Em seguida, use grep com perl regex adicionando -P. encontrar . -exec grep -nHP '(? s) SELECT. {1,60} FROM. {1,20} nome_tabela' '{}' \;
22413 Jim

8
pcregrepestá disponível no mac combrew install pcre
Jared Beck

1
Ainda melhor: também usar -Hque imprime o nome do arquivo antes de cada partida: pcregrep -HM.
Ciro Santilli escreveu:

97

Por que você não usa o awk :

awk '/Start pattern/,/End pattern/' filename

2
Isso é muito mais fácil de entender e usa awkque vem com a maioria dos sistemas * nix.
Ali Karbassi

24
legais! existe uma maneira de tornar esse jogo não ganancioso?
22468

3
Como você imprimiria o nome do arquivo apenas quando houver uma correspondência?
bibstha

2
Você pode mostrar os números de linha das correspondências com awk '/Start pattern/,/End pattern/ {printf NR " "; print}' filename. Você pode torná-la mais bonita, dando os números de linha uma largura fixa: awk '/Start pattern/,/End pattern/ {printf "%-4s ", NR; print}' filename.
Robert

Isso parece funcionar bem em um único arquivo; no entanto, e se eu quiser pesquisar em vários arquivos?
Jinstrong

84

Aqui está o exemplo usando o GNUgrep :

grep -Pzo '_name.*\n.*_description'

-z/ --null-dataTrate os dados de entrada e saída como sequências de linhas.

Veja também aqui


1
Isso explica apenas um personagem de nova linha, eu acho.
Nuvem

1
Não pude usar o grep para -zpesquisa em várias linhas, sem usar sinalizadores para não dividir a pesquisa em uma única linha e -oimprimir apenas a parte correspondente.
bbaja42

Descobri que -o causou a não imprimir nada, mas -l trabalhou para obter uma lista de arquivos (meu comando foi grep -rzl pattern *, -rzo não funcionou)
Benubird

5
Eu recomendo '' grep -Pazo '' em vez de '' -Pzo '' para arquivos não ASCII. É melhor porque a opção -z em arquivos não ASCII pode disparar o comportamento de "dados binários" do grep, que altera os valores de retorno. Mudar '' -a | --text '' impede isso.
Rloth

Não funciona no Mac com o git instalado porbrew reinstall --with-pcre git
Quanlong 15/06

21

grep -Ptambém usa libpcre, mas é muito mais amplamente instalado. Para encontrar uma titleseção completa de um documento html, mesmo que ele se estenda por várias linhas, você pode usar o seguinte:

grep -P '(?s)<title>.*</title>' example.html

Como o projeto PCRE é implementado no padrão perl, use a documentação perl para referência:


Hmm tentou isso só agora e não parecem funcionar ... gist.github.com/rdp/0286d91624930bd11d0169d6a6337c33
rogerdpack

Eu não sabia que o grep tinha essa opção. Provavelmente por causa disso: Isso é altamente experimental e o grep -P pode avisar sobre recursos não implementados. ; isso está no CentOS 7. No Fedora 29: Isso é experimental e o grep -P pode avisar sobre recursos não implementados . Claro que no BSD grep não existe. Seria bom se não fosse tão experimental, mas é bom lembrar disso - embora eu provavelmente o use.
Pryftan

17

Aqui está um exemplo mais útil:

pcregrep -Mi "<title>(.*\n){0,5}</title>" afile.html

Ele pesquisa a tag title em um arquivo html, mesmo que abranja até 5 linhas.

Aqui está um exemplo de linhas ilimitadas:

pcregrep -Mi "(?s)<title>.*</title>" example.html 

4
obrigado por isso. Fiquei sem perceber que um curinga não corresponderia ao caractere de nova linha.
22611 matt

7
@ Matt: você também pode persuadir o curinga ponto para corresponder newlines se você adicionar (?s)a sua expressão regular, assim:"(?s)<html>.*</html>"
lubomir.brindza

@matt É claro que você pode procurar $(no final de um padrão) para indicar que é o fim da linha - embora isso não seja o mesmo que ajudá-lo a encontrar vários padrões de linha. Veja também glob(7). Você também pode encontrar este site de seu interesse: regular-expressions.info
Pryftan


4

Você pode usar a alternativa grep peneira aqui (disclaimer: Eu sou o autor).

Ele suporta correspondência multilinha e limita a pesquisa a tipos de arquivos específicos imediatamente:

sift -m --files '* .py' 'YOUR_PATTERN'

(pesquise todos os arquivos * .py pelo padrão de regex multilinha especificado)

Está disponível para todos os principais sistemas operacionais. Dê uma olhada na página de amostras para ver como ela pode ser usada para extrair valores de várias linhas de um arquivo XML.


3

Esta resposta pode ser útil:

Regex (grep) para pesquisa em várias linhas necessária

Para encontrar recursivamente, você pode usar os sinalizadores -R (recursivo) e --include (padrão GLOB). Vejo:

Use grep --exclude / - sintaxe para não grep através de certos arquivos


@ Ɖ Diamond ǤeezeƦ observe que a edição de uma postagem no LQP ( stackoverflow.com/review/low-quality-posts/19341146 ) invalida a revisão, então edite apenas se tiver certeza de que a postagem precisa ser mantida.
fedorqui 'SO stop harming'

2

@Marcin: exemplo awk não ganancioso:

awk '{if ($0 ~ /Start pattern/) {triggered=1;}if (triggered) {print; if ($0 ~ /End pattern/) { exit;}}}' filename

2
perl -ne 'print if (/begin pattern/../end pattern/)' filename

Porém, isso imprime o arquivo inteiro
Herbert

1

Usando ex/ vieditor e opção globstar (sintaxe semelhante awke sed):

ex +"/string1/,/string3/p" -R -scq! file.txt

onde aaaé o seu ponto de partida e bbbo texto final.

Para pesquisar recursivamente, tente:

ex +"/aaa/,/bbb/p" -scq! **/*.py

Nota: Para ativar a **sintaxe, execute shopt -s globstar(Bash 4 ou zsh).

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.