Como rodar o grep com múltiplos padrões AND?

80

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

grep pattern1 | grep pattern2 | ...

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

grep pattern1 & pattern2 & pattern3

Eu gostaria de usar grep único porque estou construindo argumentos dinamicamente, então tudo tem que caber em uma string. Usar filtro é recurso do sistema, não grep, então não é um argumento para ele.

Não confunda essa pergunta com:

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

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

    
por greenoldman 10.11.2012 / 08:45

8 respostas

70

agrep pode fazer isso com esta sintaxe:

agrep 'pattern1;pattern2'

Com o GNU grep , quando construído com suporte a PCRE, você pode fazer:

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

Com ast grep :

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

(adicionar .* s como <x>&<y> corresponde a sequências que correspondem a <x> e <y> exatamente , a&b nunca corresponderia, pois não há essa sequência que possa seja a e b ao 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 com awk , como já mencionado:

awk '/pattern1/ && /pattern2/'

com sed :

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

Por favor, esteja ciente de que todos eles terão uma sintaxe de expressão regular diferente.

    
por 10.11.2012 / 21:13
18

Você não especificou a versão grep, isso é importante. Alguns mecanismos de regexp permitem várias correspondências agrupadas por AND usando '&' mas este é 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 o grep por sed, awk, perl, etc. (listado em ordem de aumento de peso). Com awk, o comando seria parecido com

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

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

    
por 10.11.2012 / 09:34
7

Esta não é uma solução muito boa, mas ilustra um "truque" 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
    
por 26.11.2016 / 23:13
7

Se patterns.txt contiver 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.txt -

Para pesquisar por cadeias fixas em vez de expressões regulares, use esta variante:

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

Para imprimir todas em vez de nenhuma linha da entrada, no caso em que patterns.txt esteja vazio, substitua NR==FNR por FILENAME==ARGV[1] ou por ARGIND==1 em gawk .

    
por 18.03.2016 / 16:25
2

git grep

Aqui está a sintaxe usando git grep combinando vários padrões usando expressões Booleanas :

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

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

Se os arquivos não estiverem sob controle de versão, adicione --no-index param.

Search files in the current directory that is not managed by Git.

Verifique man git-grep para ajuda.

Veja também: Verifique se existem várias strings ou expressões regulares em um arquivo .

    
por 11.04.2018 / 02:53
1

Este comando shell pode ajudar:

eval "</dev/stdin $(printf "|grep '%s'" pattern1 pattern2)" FILE

No entanto, é mais fácil se todos os seus padrões estiverem armazenados no arquivo, para que você possa definir o seguinte apelido:

alias grep-all="cat $(xargs printf '| grep "%s"' < patterns.txt)"

e use-o como:

cat text.txt | grep-all

Você pode, é claro, modificar o alias, dependendo da sua sintaxe requerida, portanto, com esse alias:

alias grep-all="</dev/stdin $(xargs printf '|grep "%s"' < patterns.txt)"

você pode usar apenas um único comando, por exemplo

grep-all text.txt

Para mais ideias, verifique também: Combine todos os padrões do arquivo de uma só vez .

Para a operação AND por arquivo, consulte: Verifique se todas as várias strings ou expressões regulares estão em um arquivo .

    
por 22.12.2016 / 17:01
0

ripgrep

Aqui está o exemplo usando rg :

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

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

Veja também o pedido de recurso relacionado em GH-875 .

    
por 11.04.2018 / 03:19
-1

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 .

EDITAR 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+$ found in file.

bbbbbbbbb bbbb

^b+$ found in file.

hhhhhhhhhh

^h+$ found in file.

Error: ^d+$ not found in file. Exiting!

    
por 14.08.2018 / 08:51