it-swarm.com.de

Regex Lookahead für 'nicht gefolgt von' in grep

Ich versuche, für alle Instanzen von Ui\., nicht gefolgt von Line, grep zu gehen, oder auch nur den Buchstaben L.

Wie schreibe ich einen Regex, um alle Instanzen einer bestimmten Zeichenfolge zu finden, die NICHT von einer anderen Zeichenfolge gefolgt werden?

Lookaheads verwenden

grep "Ui\.(?!L)" *
bash: !L: event not found


grep "Ui\.(?!(Line))" *
nothing
84
Lee Quarella

Negative Vorhersagen, nach denen Sie suchen, erfordern ein leistungsfähigeres Werkzeug als der Standard grep. Sie benötigen einen PCRE-fähigen Grep.

Wenn Sie GNU grep haben, unterstützt die aktuelle Version die Optionen -P oder --Perl-regexp und Sie können dann die gewünschte Regex verwenden.

Wenn Sie nicht über eine ausreichend aktuelle Version von GNU grep verfügen, sollten Sie ack verwenden.

123

Die Antwort auf einen Teil Ihres Problems ist hier, und ack würde sich genauso verhalten: Ack & negative Lookahead-Fehler

Sie verwenden doppelte Anführungszeichen für grep, was es bash erlaubt, "! als Befehl zum Erweitern der Historie zu interpretieren."

Sie müssen Ihr Muster in SINGLE-QUOTES umschließen: grep 'Ui\.(?!L)' *

Siehe @ JonathanLefflers Antwort , um die Probleme mit negativen Lookaheads in standard grep zu beheben!

33
NHDaly

Sie können wahrscheinlich keine negativen Lookaheads mit grep ausführen, aber normalerweise sollten Sie mit dem "inversen" Schalter '-v' gleiches Verhalten erhalten. Auf diese Weise können Sie einen Regex für das Komplement dessen erstellen, was Sie zusammenbringen möchten, und es dann durch 2 Greps leiten.

Für den fraglichen Regex könnten Sie etwas tun

grep 'Ui\.' * | grep -v 'Ui\.L'
8
Karel Tucek

Wenn Sie eine Regex-Implementierung verwenden müssen, die keine negativen Lookaheads unterstützt, und es Ihnen nichts ausmacht, zusätzliche Zeichen * abzugleichen, können Sie negierte Zeichenklassen [^L] , abwechselnd | und Ende des String-Ankers $ .

In Ihrem Fall erledigt grep 'Ui\.\([^L]\|$\)' * die Arbeit.

  • Ui\. Entspricht der Zeichenfolge, an der Sie interessiert sind

  • \([^L]\|$\) stimmt mit jedem einzelnen Zeichen außer L überein oder es stimmt mit dem Ende der Zeile überein: [^L] oder $.

Wenn Sie mehr als nur ein Zeichen ausschließen möchten, müssen Sie nur mehr Abwechslung und Negation darauf werfen. So finden Sie a, nicht gefolgt von bc:

grep 'a\(\([^b]\|$\)\|\(b\([^c]\|$\)\)\)' *

Welches ist entweder (a gefolgt von nicht b oder gefolgt von dem Ende der Zeile: a dann [^b] Oder $) Oder (a gefolgt von b, gefolgt von entweder nicht c oder dem Ende der Zeile: a dann b , dann [^c] oder $.

Diese Art von Ausdruck wird auch mit einer kurzen Zeichenfolge ziemlich unhandlich und fehleranfällig. Sie könnten etwas schreiben, um die Ausdrücke für Sie zu generieren, aber es wäre wahrscheinlich einfacher, nur eine Regex-Implementierung zu verwenden, die negative Lookaheads unterstützt.

* Wenn Ihre Implementierung nicht erfassende Gruppen unterstützt, können Sie vermeiden, zusätzliche Zeichen zu erfassen.

3
dougcosine

Wenn Ihr grep -P oder --Perl-regexp nicht unterstützt, können Sie PCRE-fähiges grep installieren, z. "pcregrep", da keine Befehlszeilenoptionen wie GNU grep erforderlich sind, um Perl-kompatible reguläre Ausdrücke zu akzeptieren. Führen Sie einfach aus 

pcregrep "Ui\.(?!Line)"

Sie brauchen keine weitere verschachtelte Gruppe für "Line" wie in Ihrem Beispiel "Ui. (?! (Line))" - die äußere Gruppe reicht aus, wie ich oben gezeigt habe.

Lassen Sie mich noch ein weiteres Beispiel für negative Assertions geben: Wenn Sie eine Liste von Zeilen haben, die von "ipset" zurückgegeben werden, zeigt jede Zeile die Anzahl der Pakete in der Mitte der Zeile und Sie brauchen keine Zeilen mit Null-Paketen Lauf:

ipset list | pcregrep "packets(?! 0 )"

Wenn Sie Perl-kompatible reguläre Ausdrücke mögen und Perl haben, aber kein pcregrep haben oder Ihr grep -Perl-regexp nicht unterstützt, können Sie einzeilige Perl-Skripts verwenden, die auf dieselbe Weise wie grep funktionieren:

Perl -e "while (<>) {if (/Ui\.(?!Lines)/){print;};}"

Perl akzeptiert stdin genauso wie grep, z.

ipset list | Perl -e "while (<>) {if (/packets(?! 0 )/){print;};}"
0
Maxim Masiutin