Como executar o grep com vários padrões AND?


86

Gostaria de obter a correspondência de vários padrões com AND implícito entre padrões, ou seja, equivalente à execução de vários greps em uma sequência:

grep pattern1 | grep pattern2 | ...

Então, como convertê-lo em algo como?

grep pattern1 & pattern2 & pattern3

Eu gostaria de usar o grep único porque estou criando argumentos dinamicamente, para que tudo tenha que caber em uma sequência. Usar filtro é um recurso do sistema, não um grep, portanto não é um argumento para isso.


Não confunda esta pergunta com:

grep "pattern1\|pattern2\|..."

Esta é uma correspondência de vários padrões OU .



Respostas:


79

agrep pode fazer isso com esta sintaxe:

agrep 'pattern1;pattern2'

Com o GNU grep, quando criado com suporte para PCRE, você pode:

grep -P '^(?=.*pattern1)(?=.*pattern2)'

Com astgrep :

grep -X '.*pattern1.*&.*pattern2.*'

(adicionando .*s como <x>&<y>casa cadeias que correspondem tanto <x>e <y> exatamente , a&bnunca correspondem como não há tal string que pode ser tanto ae bao mesmo tempo).

Se os padrões não se sobrepuserem, você também poderá:

grep -e 'pattern1.*pattern2' -e 'pattern2.*pattern1'

A melhor maneira portátil é provavelmente awkcomo já mencionado:

awk '/pattern1/ && /pattern2/'

Com sed:

sed -e '/pattern1/!d' -e '/pattern2/!d'

Lembre-se de que todos terão sintaxe de expressão regular diferente.


1
A agrepsintaxe não está funcionando para mim ... em qual versão foi introduzida?
— Raman

O @Raman 2.04 de 1992 já o possuía. Não tenho motivos para acreditar que não estava lá desde o início. Versões mais recentes (após 1992) agreppodem ser encontradas incluídas no glimpse / webglimpse . Possivelmente você tem uma implementação diferente. No entanto, tive um erro na versão ast-grep, a opção para regexps aumentados é -X, não -A.
— Stéphane Chazelas

@ StéphaneChazelas Obrigado, eu tenho o agrep0.8.0 no Fedora 23. Parece ser diferente do agrepque você mencionou.
— Raman

1
@Raman, o seu soa como TREagrep .
— Stéphane Chazelas

2
@Techiee, or just #awk '/p1/ && /p2/ {n++}; END {print 0+n}'
— Stéphane Chazelas

19

Você não especificou a versão grep, isso é importante. Alguns mecanismos regexp permitem várias correspondências agrupadas por AND usando '&', mas esse é um recurso não padrão e não portátil. Mas, pelo menos, o GNU grep não suporta isso.

OTOH, você pode simplesmente substituir grep por sed, awk, perl, etc. (listados em ordem de aumento de peso). Com awk, o comando pareceria

awk '/ regexp1 / && / regexp2 / && / regexp3 / {print; } '

e pode ser construído para ser especificado na linha de comando de maneira fácil.


3
Lembre-se de que awkusa EREs, por exemplo, o equivalente a grep -E, em oposição aos BREs que a planície grepusa.
— jw013

3
awkAs expressões regulares são chamadas de EREs, mas na verdade são um pouco idiossincráticas. Provavelmente, existem mais detalhes do que qualquer um se importa: wiki.alpinelinux.org/wiki/Regex
— dubiousjim

Obrigado, grep 2.7.3 (openSUSE). Eu votei com você, mas vou manter a pergunta em aberto por um tempo, talvez haja algum truque para grep (não que eu não goste awk- simplesmente saber mais é melhor).
— greenoldman

2
A ação padrão é imprimir a linha correspondente para que a { print; }peça não seja realmente necessária ou útil aqui.
— Tripleee

7

Se patternscontiver um padrão por linha, você poderá fazer algo assim:

awk 'NR==FNR{a[$0];next}{for(i in a)if($0!~i)next}1' patterns -

Ou isso corresponde a substrings em vez de expressões regulares:

awk 'NR==FNR{a[$0];next}{for(i in a)if(!index($0,i))next}1' patterns -

Para imprimir todos em vez de há linhas de entrada no caso que patternsestá vazio, substitua NR==FNRcom FILENAME==ARGV[1], ou com ARGIND==1no gawk.

Essas funções imprimem as linhas de STDIN que contêm cada sequência especificada como argumento como uma substring. gasignifica grep all e gaiignora case.

ga(){ awk 'FILENAME==ARGV[1]{a[$0];next}{for(i in a)if(!index($0,i))next}1' <(printf %s\\n "$@") -; }
gai(){ awk 'FILENAME==ARGV[1]{a[tolower($0)];next}{for(i in a)if(!index(tolower($0),i))next}1' <(printf %s\\n "$@") -; }

7

Essa não é uma solução muito boa, mas ilustra um "truque" bem legal

function chained-grep {
    local pattern="$1"
    if [[ -z "$pattern" ]]; then
        cat
        return
    fi    

    shift
    grep -- "$pattern" | chained-grep "$@"
}

cat something | chained-grep all patterns must match order but matter dont

1
Use chained-grep()ou function chained-grepnão function chained-grep(): unix.stackexchange.com/questions/73750/…
— nisetama 19/01

3

git grep

Aqui está a sintaxe usando a git grepcombinação de vários padrões usando expressões booleanas :

git grep --no-index -e pattern1 --and -e pattern2 --and -e pattern3

O comando acima imprimirá linhas correspondentes a todos os padrões de uma só vez.

--no-index Pesquise arquivos no diretório atual que não é gerenciado pelo Git.

Procure man git-grepajuda.

Veja também:

Para operação OR , consulte:


1

ripgrep

Aqui está o exemplo usando rg:

rg -N '(?P<p1>.*pattern1.*)(?P<p2>.*pattern2.*)(?P<p3>.*pattern3.*)' file.txt

É uma das ferramentas de grepping mais rápidas, pois é construída sobre o mecanismo de expressão regular da Rust, que usa autômatos finitos, SIMD e otimizações literais agressivas para tornar a pesquisa muito rápida.

Consulte também solicitação de recurso relacionado no GH-875 .


1

Aqui está minha opinião, e isso funciona para palavras em várias linhas:

Use find . -type fseguido por tantas
-exec grep -q 'first_word' {} \;
e a última palavra-chave com
-exec grep -l 'nth_word' {} \;

-q
-larquivos de exibição silenciosos / silenciosos

A seguinte lista de nomes de arquivos com as palavras 'rabbit' e 'hole':
find . -type f -exec grep -q 'rabbit' {} \; -exec grep -l 'hole' {} \;


-2

Para encontrar TODAS as palavras (ou padrões), você pode executar o grep no loop FOR . A principal vantagem aqui é pesquisar a partir de uma lista de regexs .

EDITE minha resposta com um exemplo real:

# search_all_regex_and_error_if_missing.sh 

find_list="\
^a+$ \
^b+$ \
^h+$ \
^d+$ \
"

for item in $find_list; do
   if grep -E "$item" file_to_search_within.txt 
   then
       echo "$item found in file."
   else
       echo "Error: $item not found in file. Exiting!"
       exit 1
   fi
done

Agora vamos executá-lo neste arquivo:

hhhhhhhhhh

aaaaaaa

bbbbbbbbb

ababbabaabbaaa

ccccccc

dsfsdf

bbbb

cccdd

aa

caa

# ./search_all_regex_and_error_if_missing.sh

aaaaaaa aa

^ a + $ encontrado no arquivo.

bbbbbbbbb bbbb

^ b + $ encontrado no arquivo.

hhhhhhhhhh

^ h + $ encontrado no arquivo.

Erro: ^ d + $ não encontrado no arquivo. Saindo!


1
Sua lógica está com defeito - perguntei pelo ALLoperador, seu código funciona como ORoperador, não AND. E aliás. pois that ( OR) é uma solução muito mais fácil, dada diretamente na pergunta.
— greenoldman

@greenoldman A lógica é simples: o for fará um loop em TODAS as palavras / padrões da lista e, se for encontrado no arquivo - o imprimirá. Portanto, basta remover o else se você não precisar de ação, caso a palavra não tenha sido encontrada.
— Noam Manos

1
Eu entendo sua lógica e minha pergunta - eu estava perguntando sobre o ANDoperador, o que significa que o arquivo é apenas um resultado positivo se corresponder ao padrão A e o padrão B e padrão C e ... ANDNo seu caso, o arquivo será positivo se corresponder padrão A ou padrão B ou ... Você vê a diferença agora?
— greenoldman

@greenoldman não sabe por que você acha que esse loop não verifica E condição para todos os padrões? Então, editei minha resposta com um exemplo real: ele procurará no arquivo todas as expressões regulares da lista e, na primeira que estiver faltando, sairá com erro.
— Noam Manos

Você está bem diante dos seus olhos, tem uma correspondência positiva logo após a primeira correspondência ser executada. Você deve "coletar" todos os resultados e computá AND-los. Então você deve reescrever o script para executar em vários arquivos - então talvez você perceba que a pergunta já foi respondida e sua tentativa não traz nada à mesa, desculpe.
— greenoldman
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.