grep string wo die nächste Zeile keinen String enthält

5

Ich möchte alle Dateien innerhalb eines Verzeichnisses und seiner Unterverzeichnisse nach Zeilen durchsuchen, die eine bestimmte Zeichenfolge enthalten, aber ich möchte diejenigen Ergebnisse ausschließen, die unmittelbar danach eine andere bestimmte Zeichenfolge in der Zeile enthalten.

Zum Beispiel das:

foo1 searchString bar
foo1 excludeString bar

foo2 searchString bar
something else

foo3 searchString bar

foo3 excludeString bar

foo4 searchString bar

sollte dies zurückgeben:

foo2 searchString bar
foo3 searchString bar
foo4 searchString bar

Ich weiß, dass -Amehrere Zeilen gedruckt werden und das -vErgebnisse ausschließt. Aber meine derzeitige Herangehensweise grep -r -A 1 "searchString" | grep -v "excludeString"kann offensichtlich nicht funktionieren.

Gibt es eine Möglichkeit, dem zweiten grep mitzuteilen, dass die vorherige Zeile ebenfalls entfernt werden soll, wenn eine Übereinstimmung gefunden wird? Oder auf eine andere Weise, wie ich das erreichen könnte?

Leistung ist nicht mein Hauptanliegen; Es wäre schön, wenn der Befehl relativ leicht zu merken ist.

tim
quelle
PS: Ich habe diese Frage gesehen, aber 1. es wird nicht rekursiv in Verzeichnissen gesucht und 2. die beiden Suchzeichenfolgen hängen voneinander ab, während meine feststehen. Ich hoffe, das könnte die Dinge vereinfachen.
Tim
Schauen Sie sich diese Antwort an, um herauszufinden , wo ein Grep aufgehört hat. Sie können verwenden -m, um einen "Cursor" aus dem ersten Grep zu pflegen und dann den zweiten Grep auszuführen. Wenn Sie dies in eine Schleife einfügen, erhalten Sie möglicherweise die gewünschte Funktionalität.
WAF

Antworten:

8

Sie können perl compatible regular expressions verwenden grep:

$ pcregrep -M '(searchString.*\n)(?!.*excludeString)' file
foo2 searchString bar
foo3 searchString bar
foo4 searchString bar

Es wird gesucht, searchStringgefolgt von einem beliebigen Zeichen ., das null oder mehrmals wiederholt wird *, gefolgt von einer neuen Zeile \n , wenn sich kein ( ?!) Muster .*excludeStringdaneben befindet. Option -Mist vorhanden, um mehrere Zeilen abzugleichen.

jimmij
quelle
Vielen Dank! Ich habe gerade pcregrep gefunden und hatte grep -r -A 1 "searchString" | pcregrep -v -M "\n.*excludeString"welche Art von funktioniert, aber Ihr Befehl ist kürzer und die Ausgabe ist schöner.
Tim
1
Streng genommen .ist "any char" aber newline , sonst würde das nicht funktionieren.
Stéphane Chazelas
1
aka grep -P(GNU): grep -Pzo '(?m)(searchString.*\n)(?!.*excludeString)' file(aber ich konnte nicht verhindern, dass zusätzliche Zeilen ausgegeben wurden).
Dennis Williamson
@DennisWilliamson Nah dran, aber die ganze Linie mit searchStringbesser abzustimmen ist grep -Pzo '(.*searchString.*$)(?!\n.*excludeString)'.
Jimmy
Vielen Dank. Das ist eine Variante, die ich nicht ausprobiert habe (ohne (?m)).
Dennis Williamson
6

Mit sed:

sed '/searchString/!d;$!N;/\n.*excludeString/!P;D' infile

Wie es funktioniert:

  • /searchString/!dlöscht die Zeile, wenn sie nicht übereinstimmt searchStringund liest eine neue Zeile ein, wodurch der Befehlszyklus erneut gestartet wird (dh die verbleibenden Befehle werden nicht mehr ausgeführt)
  • Wenn die Zeile übereinstimmt searchString, wird sedausgeführt $!N;/\n.*excludeString/!P;D- siehe HIER, wie es funktioniert. Der Unterschied besteht darin, dass hier das Muster excludeStringnach dem \newline-Zeichen gesucht wird, so dass eine Zeile mit beiden übereinstimmt searchStringund excludeStringweiterhin gedruckt wird, wenn keine Zeilenübereinstimmung folgt excludeString. Wenn es keine Zeile gab, die sowohl mit searchStringals auch excludeString(dh der bekannten Eingabe) übereinstimmt, können Sie den \n.*Teil löschen und ausführen:
    sed '/searchString/!d;$!N;/excludeString/!P;D' infile
don_crissti
quelle
2
Ich habe zehn Minuten mit der Manpage gebraucht, um das oben Genannte zu verstehen, und ich stelle mir vor, dass es vielen unserer Benutzer schwerer fällt als mir. Immerhin haben Mitglieder gesagt, dass sie Probleme beim Lesen von Manpages haben (und wer kann die Schuld geben?) Sie?). Wie Sie wissen, suchen wir nach langen Antworten, die einige Erklärungen und Zusammenhänge liefern. Ich hatte korrekte einzeilige Antworten, die weniger kryptisch waren, als dies aufgrund mangelnder Erklärung der Fall war. Würden Sie gerne Ihre Antwort analysieren und sagen, was die einzelnen Teile bewirken? (Zum Beispiel, ich brauchte ein paar Minuten nur, um herauszufinden , warum Ihr letzten Befehl war , Dstatt d.)
G-Man
1
@ G-Man - danke für deine Eingabe. Du hast recht, ich habe eine kurze Erklärung hinzugefügt. Lassen Sie mich wissen, ob es jetzt besser oder immer noch kryptisch ist.
don_crissti
Ja das ist besser
G-Man