Wenn ein regulärer Ausdruck Gruppen enthält, kann es mehrere Möglichkeiten geben, eine Zeichenfolge damit abzugleichen: reguläre Ausdrücke mit Gruppen sind nicht eindeutig. Betrachten Sie beispielsweise den regulären Ausdruck ^.*\([0-9][0-9]*\)$
und die Zeichenfolge a12
. Es gibt zwei Möglichkeiten:
- Spiel
a
gegen .*
und 2
gegen [0-9]*
; 1
passt zu [0-9]
.
- Spiel
a1
gegen .*
und die leere Zeichenfolge gegen [0-9]*
; 2
passt zu [0-9]
.
Sed wendet wie alle anderen regulären Ausdrücke die früheste Regel für die längste Übereinstimmung an: Zunächst wird versucht, den ersten Teil variabler Länge mit einer möglichst langen Zeichenfolge abzugleichen. Wenn es einen Weg findet, den Rest der Zeichenkette mit dem Rest des regulären Ausdrucks abzugleichen, ist das in Ordnung. Andernfalls versucht sed die nächstlängste Übereinstimmung für den ersten Teil variabler Länge und versucht es erneut.
Hier ist die Übereinstimmung mit der längsten Zeichenfolge zuerst a1
dagegen .*
, sodass die Gruppe nur übereinstimmt 2
. Wenn Sie möchten, dass die Gruppe früher startet, können Sie mit einigen Regexp-Engines .*
weniger gierig werden, aber sed hat keine solche Funktion. Sie müssen also die Mehrdeutigkeit mit einem zusätzlichen Anker beseitigen . Geben Sie an, dass die führende .*
Ziffer nicht mit einer Ziffer enden darf, sodass die erste Ziffer der Gruppe die erste mögliche Übereinstimmung ist.
Wenn die Zifferngruppe nicht am Zeilenanfang stehen kann:
sed -n 's/^.*[^0-9]\([0-9][0-9]*\).*/\1/p'
Wenn sich die Zifferngruppe am Anfang der Zeile befinden kann und Ihre sed den \?
Operator für optionale Teile unterstützt:
sed -n 's/^\(.*[^0-9]\)\?\([0-9][0-9]*\).*/\1/p'
Wenn sich die Zifferngruppe am Anfang der Zeile befinden kann, halten Sie sich an reguläre reguläre Ausdrücke:
sed -n -e 's/^.*[^0-9]\([0-9][0-9]*\).*/\1/p' -e t -e 's/^\([0-9][0-9]*\).*/\1/p'
Übrigens ist es dieselbe früheste längste Übereinstimmungsregel, die [0-9]*
die Ziffern nach der ersten und nicht nach der folgenden übereinstimmt .*
.
Beachten Sie, dass Ihr Programm bei mehreren Ziffernfolgen in einer Zeile immer die letzte Ziffernfolge extrahiert, da die Regel mit der frühesten längsten Übereinstimmung auf die Initiale angewendet wird .*
. Wenn Sie die erste Folge von Ziffern extrahieren möchten, müssen Sie angeben, dass eine Folge von Nicht-Ziffern vorkommt.
sed -n 's/^[^0-9]*\([0-9][0-9]*\).*$/\1/p'
Um die erste Übereinstimmung eines regulären Ausdrucks zu extrahieren, müssen Sie im Allgemeinen die Negation dieses regulären Ausdrucks berechnen. Während dies theoretisch immer möglich ist, wächst die Größe der Negation exponentiell mit der Größe des zu negierenden regulären Ausdrucks, was häufig unpraktisch ist.
Betrachten Sie Ihr anderes Beispiel:
sed -n 's/.*\(CONFIG_[a-zA-Z0-9_]*\).*/\1/p'
In diesem Beispiel tritt das gleiche Problem auf, das jedoch bei typischen Eingaben nicht auftritt. Wenn Sie es füttern hello CONFIG_FOO_CONFIG_BAR
, wird der obige Befehl gedruckt CONFIG_BAR
, nicht CONFIG_FOO_CONFIG_BAR
.
Es gibt eine Möglichkeit, das erste Match mit sed auszudrucken, aber es ist ein bisschen knifflig:
sed -n -e 's/\(CONFIG_[a-zA-Z0-9_]*\).*/\n\1/' -e T -e 's/^.*\n//' -e p
(Angenommen, Ihre sed-Unterstützung \n
bedeutet eine neue Zeile im s
Ersetzungstext.) Dies funktioniert, weil sed nach der frühesten Übereinstimmung des regulären Ausdrucks sucht und wir nicht versuchen, die Übereinstimmung zu finden, die dem CONFIG_…
Bit vorausgeht . Da sich in der Zeile kein Zeilenumbruch befindet, können wir ihn als temporären Marker verwenden. Der T
Befehl sagt aufgeben, wenn der vorhergehende s
Befehl nicht übereinstimmt.
Wenn Sie nicht wissen, wie man etwas in sed macht, wenden Sie sich an awk. Der folgende Befehl gibt die früheste längste Übereinstimmung eines regulären Ausdrucks aus:
awk 'match($0, /[0-9]+/) {print substr($0, RSTART, RLENGTH)}'
Wenn Sie es einfach halten möchten, verwenden Sie Perl.
perl -l -ne '/[0-9]+/ && print $&' # first match
perl -l -ne '/^.*([0-9]+)/ && print $1' # last match