Come eseguire grep con più pattern AND?


86

Vorrei ottenere la corrispondenza multi-pattern con AND implicito tra i pattern, ovvero equivalente a eseguire più greps in una sequenza:

grep pattern1 | grep pattern2 | ...

Quindi, come convertirlo in qualcosa del genere?

grep pattern1 & pattern2 & pattern3

Vorrei usare single grep perché sto sviluppando argomenti in modo dinamico, quindi tutto deve stare in una stringa. L'uso del filtro è una funzionalità di sistema, non grep, quindi non è un argomento per questo.


Non confondere questa domanda con:

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

Questa è una partita multi-pattern OR .



Risposte:


79

agrep puoi farlo con questa sintassi:

agrep 'pattern1;pattern2'

Con GNU grep, quando creato con il supporto PCRE, puoi fare:

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

Con astgrep :

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

(l'aggiunta di .*s come <x>&<y>stringhe corrispondenti a entrambe <x>e <y> esattamente , a&bnon corrisponderebbe mai in quanto non esiste una stringa simile che può essere entrambe ae bcontemporaneamente).

Se i motivi non si sovrappongono, potresti anche essere in grado di fare:

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

Il miglior modo portatile è probabilmente awkcome già menzionato:

awk '/pattern1/ && /pattern2/'

Con sed:

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

Si noti che tutti questi avranno una diversa sintassi delle espressioni regolari.


1
La agrepsintassi non funziona per me ... in quale versione è stata introdotta?
Raman,

@Raman 2.04 del 1992 ce l' aveva già. Non ho motivo di credere che non ci fosse dall'inizio. Le versioni più recenti (dopo il 1992) agreppossono essere trovate incluse in glimpse / webglimpse . Forse hai un'implementazione diversa. Ho avuto un errore per la versione AST-grep, però, l'opzione per espressioni regolari aumentata è -X, non è -A.
Stéphane Chazelas,

@ StéphaneChazelas Grazie, ho agrep0.8.0 su Fedora 23. Questo sembra essere diverso agrepda quello a cui fai riferimento.
Raman,

1
@Raman, il tuo suona come TREagrep .
Stéphane Chazelas,

2
@Techiee, o semplicementeawk '/p1/ && /p2/ {n++}; END {print 0+n}'
Stéphane Chazelas,

19

Non hai specificato la versione grep, questo è importante. Alcuni motori regexp consentono una corrispondenza multipla supportata da AND usando '&' ma questa è una funzionalità non standard e non portatile. Ma almeno GNU grep non lo supporta.

OTOH puoi semplicemente sostituire grep con sed, awk, perl, ecc. (Elencati in ordine di aumento di peso). Con awk, il comando sarebbe simile

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

e può essere costruito per essere specificato nella riga di comando in modo semplice.


3
Ricorda solo che awkutilizza ERE, ad esempio l'equivalente di grep -E, in contrapposizione ai BRE che semplici grepusi.
jw013,

3
awkLe regex sono chiamate ERE, ma in realtà sono un po 'idiosincratiche. Qui probabilmente ci sono più dettagli di quanti se ne preoccupino: wiki.alpinelinux.org/wiki/Regex
dubiousjim

Grazie, grep 2.7.3 (openSUSE). Ti ho votato, ma terrò la domanda aperta per un po ', forse c'è qualche trucco per grep (non che non mi piaccia awk- semplicemente sapere di più è meglio).
Greenoldman,

2
L'azione predefinita è stampare la linea corrispondente in modo che la { print; }parte non sia realmente necessaria o utile qui.
tripleee

7

Se patternscontiene un motivo per riga, puoi fare qualcosa del genere:

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

O questo corrisponde a sottostringhe invece di espressioni regolari:

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

Per stampare tutto anziché nessuna riga dell'input nel caso che patternssia vuoto, sostituire NR==FNRcon FILENAME==ARGV[1]o con ARGIND==1in gawk.

Queste funzioni stampano le righe di STDIN che contengono ciascuna stringa specificata come argomento come sottostringa. gasta per grep all e gaiignora il caso.

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

Questa non è un'ottima soluzione ma illustra un "trucco" piuttosto interessante

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
Usa chained-grep()o function chained-grepno function chained-grep(): unix.stackexchange.com/questions/73750/…
nisetama

3

git grep

Ecco la sintassi usando la git grepcombinazione di più schemi usando espressioni booleane :

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

Il comando sopra stampa le linee corrispondenti a tutti i motivi contemporaneamente.

--no-index Cerca i file nella directory corrente che non è gestita da Git.

Cerca man git-grepaiuto.

Guarda anche:

Per il funzionamento OR , vedere:


1

ripgrep

Ecco l'esempio usando rg:

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

È uno degli strumenti più veloci per il grepping, poiché è basato sul motore regex di Rust che utilizza automi finiti, SIMD e ottimizzazioni letterali aggressive per rendere la ricerca molto veloce.

Vedere anche la richiesta di funzionalità correlate su GH-875 .


1

Ecco la mia opinione, e questo funziona con le parole in più righe:

Usa find . -type fseguito da altrettante
-exec grep -q 'first_word' {} \;
e l'ultima parola chiave con
-exec grep -l 'nth_word' {} \;

-q
-lmostra file silenziosi / silenziosi con corrispondenze

Di seguito viene riportato un elenco di nomi di file con le parole "coniglio" e "buca" in essi:
find . -type f -exec grep -q 'rabbit' {} \; -exec grep -l 'hole' {} \;


-2

Per trovare TUTTE le parole (o motivi), puoi eseguire grep nel ciclo FOR . Il vantaggio principale qui è la ricerca da un elenco di regex .

MODIFICA la mia risposta con un esempio reale:

# 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

Ora eseguiamolo su questo file:

hhhhhhhhhh

aaaaaaa

bbbbbbbbb

ababbabaabbaaa

ccccccc

dsfsdf

bbbb

cccdd

aa

caa

# ./search_all_regex_and_error_if_missing.sh

aaaaaaa aa

^ a + $ trovato nel file.

bbbbbbbbb bbbb

^ b + $ trovato nel file.

hhhhhhhhhh

^ h + $ trovati nel file.

Errore: ^ d + $ non trovato nel file. Uscita!


1
La tua logica è difettosa - ho chiesto ALLoperatore, il tuo codice funziona come ORoperatore, no AND. E a proposito. per quello ( OR) è la soluzione molto più semplice data proprio nella domanda.
Greenoldman,

@greenoldman La logica è semplice: for for eseguirà il ciclo su TUTTE le parole / motivi nell'elenco e, se si trova nel file, lo stamperà. Quindi rimuovi l'altro se non hai bisogno di azioni nel caso in cui word non fosse trovato.
Noam Manos,

1
Capisco la tua logica e la mia domanda: stavo chiedendo ANDdell'operatore, il che significa che il file è solo un successo se corrisponde al modello A e al modello B e al modello C e ... ANDNel tuo caso il file è un colpo positivo se corrisponde modello A o modello B o ... Vedi la differenza adesso?
Greenoldman,

@greenoldman non sei sicuro del motivo per cui pensi che questo ciclo non controlli la condizione AND per tutti i modelli? Quindi ho modificato la mia risposta con un esempio reale: cercherà nel file tutta la regex dell'elenco e sul primo che manca - uscirà con errore.
Noam Manos,

Lo hai davanti ai tuoi occhi, hai una corrispondenza positiva subito dopo l'esecuzione della prima partita. Dovresti "raccogliere" tutti i risultati e calcolarli AND. Quindi dovresti riscrivere lo script per eseguirlo su più file, quindi forse ti rendi conto che la domanda ha già una risposta e il tuo tentativo non porta nulla sul tavolo, mi dispiace.
Greenoldman,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.