Wie mache ich ein nicht gieriges Match in grep?

Antworten:

276

Sie suchen ein nicht gieriges (oder faules) Match. Um eine nicht gierige Übereinstimmung in regulären Ausdrücken zu erhalten, müssen Sie den Modifikator ?nach dem Quantifizierer verwenden. Zum Beispiel können Sie ändern .*zu.*? .

Standardmäßig werden grepkeine nicht gierigen Modifikatoren unterstützt, Sie können grep -Pjedoch die Perl-Syntax verwenden.

Mark Byers
quelle
3
eegg: dot all modifier wird auch als mehrzeilig bezeichnet. Es ist ein Modifikator, der das "." Ändert. Übereinstimmungsverhalten, um Zeilenumbrüche einzuschließen (normalerweise nicht). Es gibt keinen solchen Modifikator in grep, aber in pcregrep .
A. Wilson
1
Korrektur: In den meisten Regex-Varianten, die dies unterstützen, wird der Modus, mit .dem Zeilenumbrüche abgeglichen werden können, als DOTALL- oder Einzeilenmodus bezeichnet . Ruby ist der einzige, der es mehrzeilig nennt . In den anderen Varianten ist Multiline der Modus, in dem die Anker ( ^und $) an den Liniengrenzen übereinstimmen können. Ruby hat keinen äquivalenten Modus, da sie in Ruby immer so funktionieren.
Alan Moore
5
-Pwar eine völlig neue für mich, ich habe mich jahrelang glücklich gefreut und nur -E... so viele verschwendete Jahre benutzt! - Hinweis für sich selbst: Lesen Sie Manpages als (noch mehr!) Normale Sache neu, Sie verdauen nie genug Schalter und Optionen.
ocodo
29
Auf einigen Plattformen (wie Mac OS X) wird grepdies nicht unterstützt -P. Wenn Sie es jedoch verwenden egrep, können Sie das .*?Muster verwenden, um das gleiche Ergebnis zu erzielen. egrep -o 'start.*?end' text.html
SaltyNuts
4
Als Erweiterung des Kommentars zu @SaltyNuts unterstützt Mac OS X dies nicht -P, -Ewürde jedoch aufrufen, egrepdass die vorgeschlagenen Funktionen einwandfrei .*?funktionieren.
Fredrik Erlandsson
83

Eigentlich funktioniert das .*?nur in perl. Ich bin mir nicht sicher, wie die entsprechende grep-erweiterte Regexp-Syntax aussehen würde. Glücklicherweise können Sie die Perl-Syntax mit grep verwenden, grep -Pwürde also funktionieren, aber grep -Edas ist dasselbe, egrepwas nicht funktionieren würde (es wäre gierig).

Siehe auch: http://blog.vinceliu.com/2008/02/non-greedy-regular-expression-matching.html

John Smith
quelle
9
grep -Pfunktioniert nicht in GNU grep 2.9 - habe es gerade versucht (es macht keinen Fehler, wendet nur stillschweigend das nicht an ?. Intertestly auch nicht die nicht-Klasse zB:env|grep '[^\=]*\='
roberto tomás
2
In Darwin / OS X 10.8 Mountain Lion gibt es keine grep -POption oder keinen pgrepBefehl, aber es egrepfunktioniert hervorragend.
Steve HHH
2
Es gibt einen pgrepBefehl auf meiner OS X 10.9-Box, aber es ist ein völlig anderes Programm, dessen Zweck darin besteht, "Prozesse nach Namen zu finden oder zu signalisieren".
Desty
@ robertotomás Als Antwort auf einen 6 Jahre alten Kommentar hier, aber ... Ich dachte das auch und stellte dann fest, dass ich mehrere nicht gierige Matches bekam. Auf einem Farbterminal können Sie beispielsweise das Echo "bbbbb" | sehen grep -P 'b. *? b'` gibt 2 Übereinstimmungen zurück.
zzxyz
12

Mein grep, der funktioniert, nachdem ich Sachen in diesem Thread ausprobiert habe:

echo "hi how are you " | grep -shoP ".*? "

Stellen Sie einfach sicher, dass Sie jeder Ihrer Zeilen ein Leerzeichen hinzufügen

(Meins war eine zeilenweise Suche, um Wörter auszuspucken)

Jonz
quelle
3
-shoPschöne Gedächtnisstütze :)
Mariusz
echo "bbbbb" | grep -shoP 'b.*?b'ist ein bisschen eine Lernerfahrung. Einzige Sache, die bei mir auch explizit faul funktioniert hat.
Zzxyz
12

grep

Für nicht gierige Übereinstimmungen in können grepSie eine negierte Zeichenklasse verwenden. Versuchen Sie mit anderen Worten, Platzhalter zu vermeiden.

Um beispielsweise alle Links zu JPEG-Dateien aus dem Seiteninhalt abzurufen, verwenden Sie Folgendes:

grep -o '"[^" ]\+.jpg"'

Um mit mehreren Zeilen umzugehen, leiten Sie die Eingabe xargszuerst durch . Verwenden Sie für die Leistung ripgrep.

Kenorb
quelle
3

Die kurze Antwort verwendet den nächsten regulären Ausdruck:

(?s)<car .*? model=BMW .*?>.*?</car>
  • (? s) - Dies entspricht einer mehrzeiligen Übereinstimmung
  • . *? - stimmt mit jedem Charakter mehrmals faul überein (minimale Übereinstimmung)

Eine (etwas) kompliziertere Antwort lautet:

(?s)<([a-z\-_0-9]+?) .*? model=BMW .*?>.*?</\1>

Dies ermöglicht es, car1 und car2 im folgenden Text abzugleichen

<car1 ... model=BMW ...>
...
...
...
</car1>
<car2 ... model=BMW ...>
...
...
...
</car2>
  • (..) repräsentiert eine Erfassungsgruppe
  • \ 1 stimmt in diesem Zusammenhang mit dem Sametext überein, der zuletzt durch Erfassen der Gruppennummer 1 abgeglichen wurde
jmc
quelle
1

Entschuldigung, ich bin 9 Jahre zu spät, aber das könnte für die Zuschauer im Jahr 2020 funktionieren.

Angenommen, Sie haben eine Zeile wie "Hello my name is Jello". Jetzt möchten Sie die Wörter finden, die mit 'H'und 'o'mit einer beliebigen Anzahl von Zeichen dazwischen beginnen und enden . Und wir wollen keine Zeilen, wir wollen nur Worte. Dafür können wir den Ausdruck verwenden:

grep "H[^ ]*o" file

Dies gibt alle Wörter zurück. Dies funktioniert folgendermaßen: Es werden alle Zeichen anstelle von Leerzeichen dazwischen zugelassen. Auf diese Weise können wir mehrere Wörter in derselben Zeile vermeiden.

Jetzt können Sie das Leerzeichen durch ein beliebiges anderes Zeichen ersetzen. Angenommen, die erste Zeile war "Hello-my-name-is-Jello", dann können Sie Wörter mit dem Ausdruck erhalten:

grep "H[^-]*o" file
mr.1n5an_e
quelle
0

Ich weiß, dass es ein bisschen tot ist, aber ich habe gerade bemerkt, dass das funktioniert. Es hat sowohl Bereinigung als auch Bereinigung von meiner Ausgabe entfernt.

> grep -v -e 'clean\-\?up'
> grep --version grep (GNU grep) 2.20
user200850
quelle