Grep (et al): match all patterns

Hi,

ein fiktives Programm, das unglaublich lange laeuft und dabei Millionen Gigabyte Text nach stdout schreibt (heisst: ich will das Programm nicht mehrmals laufen lasse und moechte die Ausgabe nirgendwo zwischenspeichern, schon gar nicht im RAM) war nur erfolgreich, wenn in seiner Ausgabe eine nicht naeher definierte Anzahl bestimmter regulaerer Ausdruecke auftaucht. In meiner Naivitaet dachte ich zuerst, ich koennte bei

 $ ./a.out |grep -q -e foo -e bar -e baz

grep dazu ueberreden, die patterns mit logischem Und zu verknuepfen, aber das ist schon erfolgreich, wenn auch nur einer der Begriffe auftaucht. Irgendwer Ideen? sed, perl, awk, voellig egal, aber die Liste der Begriffe muss leicht erweiterbar sein und regulaere Ausdruecke waeren nett.

Danke im Voraus,
Gruss vom Frank.

Warum machst du nicht einfach ein

 $ ./a.out |grep -q -e foo | grep -q -e bar | grep -q -e baz

Damit hast du ja genau deine UND-Verknüpfung.

Alternativ kannst du natürlich auch probieren einfach alles in einen RegEx rein zu tun. Folgender RegEx sucht nach einem Text der Foo, Bar und Baz enthält. Reihenfolge ist wurscht. Die Zeilenumbrüche gehören natürlich raus, die hab ich nur reingetan, damit man es besser nachvollziehen kann:

./a.out | grep -q -e 
"((Foo)(.\*)(Bar)(.\*)(Baz))|((Foo)(.\*)(Baz)(.\*)(Bar))|
((Baz)(.\*)(Foo)(.\*)(Bar))|((Baz)(.\*)(Bar)(.\*)(Foo))|
((Bar)(.\*)(Foo)(.\*)(Baz))|((Bar)(.\*)(Baz)(.\*)(Foo))"

Allerdings wird der Ausdruck immer komplizierter, weil du ja jede Kombination der Ausdrücke berücksichtigen musst. Die Anzahl möglicher Kombinationen ist aber n!, also bei 4 wären das 4! = 4*3*2*1 = 24. Das wird also schnell nicht mehr brauchbar.

[Bei dieser Antwort wurde das Vollzitat nachträglich automatisiert entfernt]

Hallo

In meiner Naivitaet dachte ich zuerst, ich koennte bei

$ ./a.out |grep -q -e foo -e bar -e
baz

grep dazu ueberreden, die patterns mit logischem Und zu
verknuepfen, aber das ist schon erfolgreich, wenn auch nur
einer der Begriffe auftaucht. Irgendwer Ideen? sed, perl,
awk, voellig egal, aber die Liste der Begriffe muss leicht
erweiterbar sein und regulaere Ausdruecke waeren nett.

Oops, ich hatte gerade eine _OR_ verknüpfung mit egrep gepostet,
das ist es natürlich nicht. Hier weiss ich auch nicht weiter
ausser vielleicht mit Perl:

./a.out | perl -lne 'print if /(?=.\*foo)(?=.\*bar)(?=.\*baz)/'

Aber Vorsicht, das tut ‚backtracken‘ (nicht sehr fix).

Grüße

CMБ

Hallo,

$ ./a.out |grep -q -e foo | grep -q -e bar | grep
-q -e baz

Oder wenn man nicht für jedes Pattern ein eigenes grep starten will, und keine quadratische Größe im Pattern haben will:

./a.out|perl -ne 'print if /foo/ && /bar/ && /baz/'

Grüße,
Moritz

Warum machst du nicht einfach ein

$ ./a.out |grep -q -e foo | grep -q -e bar | grep -q -e baz

Damit hast du ja genau deine UND-Verknüpfung.

Das funktioniert nicht und schlaegt immer fehl: das erste grep -q gibt nichts nach stdout aus, das zweite wird niemals was zum matchen haben.

Alternativ kannst du natürlich auch probieren einfach alles in
einen RegEx rein zu tun. Folgender RegEx sucht nach einem Text
der Foo, Bar und Baz enthält. Reihenfolge ist wurscht. Die
Zeilenumbrüche gehören natürlich raus, die hab ich nur
reingetan, damit man es besser nachvollziehen kann:

./a.out | grep -q -e
„((Foo)(.*)(Bar)(.*)(Baz))|((Foo)(.*)(Baz)(.*)(Bar))|
((Baz)(.*)(Foo)(.*)(Bar))|((Baz)(.*)(Bar)(.*)(Foo))|
((Bar)(.*)(Foo)(.*)(Baz))|((Bar)(.*)(Baz)(.*)(Foo))“

Allerdings wird der Ausdruck immer komplizierter, weil du ja
jede Kombination der Ausdrücke berücksichtigen musst. Die
Anzahl möglicher Kombinationen ist aber n!, also bei 4 wären
das 4! = 4*3*2*1 = 24. Das wird also schnell nicht mehr
brauchbar.

Ja, eben. Ausserdem matcht der Ausdruck nur, wenn Foo, Bar und Baz irgendwo in einer Zeile auftauchen. Ich brauche aber, dass die Begriffe irgendwo in der Ausgabe erscheinen, also evtl. auch auf unterschiedlichen Zeilen.

Gruss vom Frank.

$ ./a.out |grep -q -e foo -e bar -e baz

Hier weiss ich auch nicht weiter ausser vielleicht mit Perl:

./a.out | perl -lne ‚print if /(?=.*foo)(?=.*bar)(?=.*baz)/‘

Das matcht AFAICS nur, wenn die Begriffe auf einer Zeile stehen. Sie stehen irgendwo im Text.

Gruss vom Frank.

$ ./a.out |grep -q -e foo -e bar -e baz

Hier weiss ich auch nicht weiter ausser vielleicht mit Perl:

./a.out | perl -lne ‚print if /(?=.*foo)(?=.*bar)(?=.*baz)/‘

Das matcht AFAICS nur, wenn die Begriffe auf einer Zeile
stehen. Sie stehen irgendwo im Text.

Aha. In irgendeiner zeile? Dann muß man
die Begriffe in einem hash speichern:

./a.out | perl -ne '$h{$1}++ while/(foo|bar|baz)/g; die "erfolg: $\_" if 3==keys %h'

Die „3 == …“ muß mit der Anzahl der (x|y|z)-Begriffe übereinstimmen

Grüße

CMБ

Hallo Frank,

ein fiktives Programm,

war nur erfolgreich, wenn in seiner Ausgabe eine nicht naeher
definierte Anzahl bestimmter regulaerer Ausdruecke auftaucht.

Irgendwer Ideen?

Ein Skriptansatz, nur leicht getestet und sicher verbesserungsfähig:

#!/bin/bash
FILE=$1
shift
while [$# -gt 0]
do
 MUSTER=$1
 grep -qwe $MUSTER $FILE
 if [$? -gt 0]
 then exit 1
 fi 
 shift
done
exit 0

Wenn Du das Skript z.B. mygrep nennst, kannst Du es folgendermassen aufrufen:

mygrep Datei Muster1 Muster2 Muster3 Muster4 ....

Wenn nur ein Muster nicht gefunden wird, bricht es ab und gibt 1 zurück, ansonsten 0.
Willst Du die datei nicht angeben, sondern von stdin lesen, ja da musst Du das ein wenig umstricken. Bin ich jetzt zu faul dazu :wink:

Viele Grüße
Marvin

Hallo,

Das matcht AFAICS nur, wenn die Begriffe auf einer Zeile
stehen. Sie stehen irgendwo im Text.

Dann solltest du deine Ausgabe erst in eine Datei umleiten, und dann sowas machen:

grep pattern1 file \> /dev/null && grep pattern2 file \> /dev/null && echo "all matched"

Vermutlich kann man auch eine subshell für alle greps öffnen, und damit den redirect nur einmal machen, aber dafür ist es mir jetzt zu spät das noch zusammenzubasteln.

Grüße,
Moritz