Como faço para pesquisar arquivos usando o grep para várias seqüências (pesquisa de interseção)

3

Como eu uso o grep para pesquisar uma estrutura de diretório aninhada por arquivos contendo todas as palavras no meu padrão de pesquisa?

Eu quero procurar arquivos que contenham várias palavras - vamos usar foo bar e bah. Eu posso fazer grep -rl foo |xargs grep -rl bah| ...etc , mas existe uma maneira mais fácil de fazer isso? Eu sei que eu posso usar -F para um arquivo de seqüências de caracteres para procurar, mas acredito que isso ainda procura as seqüências de caracteres usando um operador OR (união), e eu preciso usar o operador AND (interseção).

    
por user3.1415927 09.02.2018 / 16:34

4 respostas

3

Solução

find + awk :

find . -type f -exec awk '/\<foo\>/{ p1=1 }/\<bar\>/{ p2=1 }/\<bah\>/{ p3=1 }
                          p1 && p2 && p3{ print FILENAME; exit }' {} +

awk detalhes do programa:

  • /\<foo\>/{ p1=1 }/\<bar\>/{ p2=1 }/\<bah\>/{ p3=1 } - ao encontrar cada um dos padrões necessários - definir o respectivo sinalizador
  • p1 && p2 && p3 - assim que todos os padrões forem encontrados:
    • print FILENAME - imprime o atual nome do arquivo / caminho do arquivo
    • exit - fecha a execução do script imediatamente
por 09.02.2018 / 16:53
0

Para um E lógico como esse, geralmente recuo em awk :

awk '/foo/ && /bar/ && /bah/ { print }' /path/to/file
    
por 09.02.2018 / 16:42
0

Usando a opção GNU grep com -P (Perl-Compatibility) e positive lookahead regex (?=(regex)) para procurar as palavras em qualquer ordem em uma única linha ou em um arquivo inteiro e recursivamente em todos os arquivos encontrados diretório atual.

grep -rlP '(?s)(?=.*?\bfoo\b)(?=.*?\bbar\b)(?=.*?\bbah\b)' .
  • (?s) aqui é o modificador DOTALL e permite que ponto corresponda a até mesmo \ novas linhas, usaríamos (.|\n)*? também entre palavras, assim como [\s\S]*? .

  • em \bWORD\b ; \b são âncoras de limites de palavras.

Com a entrada da seguinte forma:

==> file1 <==
foo here and bar
bah
and of file1

==> file2 <==
foo then bar and bah

==> file3 <==
foo foobarbah ba

==> file4 <==
this is foo bar bahh
bah

A saída é:

./file1
./file2
./sub-dir/file4
    
por 09.02.2018 / 16:48
0

Minha resposta é semelhante à resposta de @ RomanPerekhrest. A principal diferença é que ele tira proveito do fato de que você pode obter awk para processar toda a entrada de uma só vez definindo o separador de registro ( RS ) como algo que nunca corresponderá a nada na entrada (por exemplo, ^$ ). Em outras palavras, sorve o arquivo inteiro e pesquise-o como se fosse uma única string.

por exemplo,

find . -type f -exec \
  awk -v RS='^$' '/foo/ && /bar/ && /baz/ { print FILENAME }' {} +

Isso listará todos os arquivos abaixo do diretório atual ( . ) que contêm ALL das expressões regulares foo , bar e baz . Se você precisar de algumas ou de todas as expressões regulares para serem tratadas como palavras inteiras, coloque-as entre as âncoras de limite de palavras \< e \> - por exemplo, \<foo\> .

Isso também é executado com mais rapidez porque não bifurca awk uma vez para cada arquivo. Em vez disso, ele executa awk com tantos argumentos de nome de arquivo que caberão no buffer de linha de comando (normalmente 128K ou 1 ou 2M caracteres em sistemas modernos). Se find descobrir 1000 arquivos, só será executado awk uma vez em vez de 1000 vezes.

Observação: isso requer uma versão de awk que permita que RS seja uma expressão regular. Veja Slurp-mode no awk? para mais detalhes e um exemplo de como implementar um forma limitada de leitura "modo slurp" em outras versões do awk.

Nota: Isto lerá todo o conteúdo de cada arquivo encontrado na memória, um de cada vez. Para arquivos verdadeiramente enormes, por ex. arquivos de log com dezenas de gigabytes ou maiores em tamanho, isso pode exceder a RAM disponível ou mesmo RAM + SWAP. Por mais improvável que seja, se acontecer, pode causar sérios problemas (por exemplo, no Linux, o kernel começará a matar processos aleatórios se for executado em RAM e SWAP).

    
por 11.02.2018 / 09:41