Wie kann man grep mit mehreren UND-Mustern ausführen?

86

Ich möchte die Mehrfachmusterübereinstimmung mit implizitem UND zwischen Mustern erhalten, dh gleichbedeutend mit dem Ausführen mehrerer Greps in einer Sequenz:

grep pattern1 | grep pattern2 | ...

Also, wie konvertiert man es in so etwas wie?

grep pattern1 & pattern2 & pattern3

Ich würde gerne single grep verwenden, weil ich Argumente dynamisch aufbaue, also muss alles in einen String passen. Die Verwendung von Filter ist eine Systemfunktion und kein Argument.


Verwechseln Sie diese Frage nicht mit:

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

Dies ist eine ODER-Verknüpfung mit mehreren Mustern.

Greenoldman
quelle

Antworten:

78

agrep kann es mit dieser Syntax tun:

agrep 'pattern1;pattern2'

Mit GNU können Sie grep, wenn es mit PCRE-Unterstützung erstellt wurde, Folgendes tun:

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

Mit astgrep :

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

(Das Hinzufügen von .*s als <x>&<y>Übereinstimmungszeichenfolgen, die beide <x>und <y> genau übereinstimmen , a&bwürde niemals übereinstimmen, da es keine solche Zeichenfolge gibt, die beide aund bgleichzeitig sein kann.)

Wenn sich die Muster nicht überlappen, können Sie möglicherweise auch Folgendes tun:

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

Der beste portable Weg ist wahrscheinlich mit awkwie schon erwähnt:

awk '/pattern1/ && /pattern2/'

Mit sed:

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

Bitte beachten Sie, dass alle diese Ausdrücke eine andere Syntax für reguläre Ausdrücke haben.

Stéphane Chazelas
quelle
1
Die agrepSyntax funktioniert bei mir nicht ... In welcher Version wurde sie eingeführt?
Raman
@Raman 2.04 von 1992 hatte es schon. Ich habe keinen Grund zu der Annahme, dass es von Anfang an nicht da war. Neuere (nach 1992) Versionen von agrepkönnen mit glimpse / webglimpse gefunden werden . Möglicherweise haben Sie eine andere Implementierung. Ich hatte einen Fehler für die Ast-Grep-Version, die Option für erweiterte reguläre Ausdrücke ist jedoch -Xnicht -A.
Stéphane Chazelas
@ StéphaneChazelas Danke, ich habe agrep0.8.0 für Fedora 23. Dies scheint eine andere zu sein agrepals die, auf die Sie verweisen.
Raman
1
@ Raman, deine klingt wie TREagrep .
Stéphane Chazelas
2
@ Techiee, oder einfachawk '/p1/ && /p2/ {n++}; END {print 0+n}'
Stéphane Chazelas
19

Sie haben keine grep-Version angegeben, dies ist wichtig. Einige reguläre Ausdrücke ermöglichen mehrere Übereinstimmungen, die durch UND mit '&' gruppiert werden. Dies ist jedoch keine Standard- und keine portable Funktion. Aber zumindest GNU grep unterstützt dies nicht.

OTOH Sie können grep einfach durch sed, awk, perl usw. ersetzen (in der Reihenfolge der Gewichtszunahme aufgeführt). Mit awk würde der Befehl so aussehen

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

und es kann so konstruiert werden, dass es auf einfache Weise in der Befehlszeile angegeben wird.

Netch
quelle
3
Denken Sie daran, dass awkEREs verwendet werden, z. B. das Äquivalent von grep -E, im Gegensatz zu den BREs, die normal grepverwendet werden.
jw013
3
awkDie regulären Ausdrücke von werden als EREs bezeichnet, aber in Wirklichkeit sind sie etwas eigenwillig. Hier sind wahrscheinlich mehr Details, als jemand interessiert: wiki.alpinelinux.org/wiki/Regex
dubiousjim
Vielen Dank, grep 2.7.3 (openSUSE). Ich habe dich aufgewertet, aber ich werde die Frage noch eine Weile offen halten, vielleicht gibt es einen Trick für grep (nicht, dass ich es nicht mag awk- einfach zu wissen, dass mehr besser ist).
Greenoldman
2
Die Standardaktion besteht darin, die übereinstimmende Zeile zu drucken, damit das { print; }Teil hier nicht wirklich notwendig oder nützlich ist.
Tripleee
7

Wenn patternsein Muster pro Zeile enthalten ist, können Sie folgendermaßen vorgehen:

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

Oder dies entspricht Teilzeichenfolgen anstelle von regulären Ausdrücken:

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

patternsErsetzen Sie NR==FNRmit FILENAME==ARGV[1]oder mit ARGIND==1in , um alle anstelle von keinen Zeilen der Eingabe in dem Fall zu drucken, der leer ist gawk.

Diese Funktionen geben die Zeilen von STDIN aus, die jede als Argument angegebene Zeichenfolge als Teilzeichenfolge enthalten. gasteht für grep all und gaiignoriert Groß- und Kleinschreibung.

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 "$@") -; }
Nisetama
quelle
7

Dies ist keine sehr gute Lösung, zeigt aber einen etwas coolen "Trick"

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
Olejorgenb
quelle
1
Verwenden Sie entweder chained-grep()oder function chained-grepaber nicht function chained-grep(): unix.stackexchange.com/questions/73750/…
nisetama
3

git grep

Hier ist die Syntax, bei git grepder mehrere Muster mithilfe von Booleschen Ausdrücken kombiniert werden :

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

Mit dem obigen Befehl werden Zeilen gedruckt, die mit allen Mustern auf einmal übereinstimmen.

--no-index Suchen Sie nach Dateien im aktuellen Verzeichnis, die nicht von Git verwaltet werden.

Suchen Sie man git-grepnach Hilfe.

Siehe auch:

Zum OP siehe:

Kenorb
quelle
1

ripgrep

Hier ist das Beispiel mit rg:

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

Es ist eines der schnellsten Grepping-Tools, da es auf Rusts Regex-Engine aufbaut, die endliche Automaten, SIMD und aggressive wörtliche Optimierungen verwendet, um die Suche sehr schnell zu machen.

Siehe auch zugehörige Funktionsanforderung bei GH-875 .

Kenorb
quelle
1

Hier ist meine Einstellung, und dies funktioniert für Wörter in mehreren Zeilen:

Verwenden Sie find . -type fgefolgt von so vielen
-exec grep -q 'first_word' {} \;
und dem letzten Schlüsselwort mit
-exec grep -l 'nth_word' {} \;

-qLeise / stille
-lDateien mit Übereinstimmungen anzeigen

Die folgende Liste enthält die Dateinamen mit den Wörtern "Hase" und "Loch":
find . -type f -exec grep -q 'rabbit' {} \; -exec grep -l 'hole' {} \;

StackRover
quelle
-2

Um ALLE Wörter (oder Muster) zu finden, können Sie in der FOR-Schleife grep ausführen . Der Hauptvorteil hierbei ist die Suche aus einer Liste von regulären Ausdrücken .

BEARBEITEN Sie meine Antwort mit einem echten Beispiel:

# 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

Lassen Sie es uns nun auf dieser Datei ausführen:

hhhhhhhhh

aaaaaaa

bbbbbbbbb

ababbabaabbaaa

ccccccc

dsfsdf

bbbb

cccdd

aa

caa

# ./search_all_regex_and_error_if_missing.sh

aaaaaaa aa

^ a + $ in Datei gefunden.

bbbbbbbbbbbb

^ b + $ in Datei gefunden.

hhhhhhhhh

^ h + $ in Datei gefunden.

Fehler: ^ d + $ nicht in Datei gefunden. Verlassen!

Noam Manos
quelle
1
Ihre Logik ist fehlerhaft - ich habe nach dem ALLOperator gefragt , Ihr Code funktioniert als OROperator, nicht AND. Und übrigens. denn das ( OR) ist viel einfacher Lösung direkt in der Frage gegeben.
Greenoldman
@greenoldman Die Logik ist einfach: Mit der for- Schleife werden ALLE Wörter / Muster in der Liste durchlaufen , und wenn sie in einer Datei gefunden werden, wird sie gedruckt. Entfernen Sie also einfach das else, wenn Sie keine Aktion benötigen, falls das Wort nicht gefunden wurde.
Noam Manos
1
Ich verstehe Ihre Logik sowie meine Frage - ich habe nach dem ANDOperator gefragt , was bedeutet, dass die Datei nur dann positiv ist, wenn sie mit Muster A und Muster B und Muster C übereinstimmt und ... ANDIn Ihrem Fall ist die Datei positiv, wenn sie übereinstimmt Muster A oder Muster B oder ... Sehen Sie jetzt den Unterschied?
Greenoldman
@greenoldman nicht sicher, warum diese Schleife Ihrer Meinung nach nicht für alle Muster die UND-Bedingung überprüft? Also habe ich meine Antwort mit einem echten Beispiel bearbeitet: Es wird in der Datei nach allen regulären Ausdrücken der Liste gesucht und die erste, die fehlt, wird mit Fehler beendet.
Noam Manos
Sie haben es direkt vor Ihren Augen, Sie haben eine positive Übereinstimmung, kurz nachdem die erste Übereinstimmung ausgeführt wurde. Sie sollten alle Ergebnisse "sammeln" und darauf rechnen AND. Dann sollten Sie das Skript so umschreiben, dass es für mehrere Dateien ausgeführt wird. Vielleicht stellen Sie dann fest, dass die Frage bereits beantwortet ist und Ihr Versuch nichts auf den Tisch bringt.
Greenoldman