sed: Nur passende Gruppe drucken

133

Ich möchte die letzten beiden Zahlen (eine int, eine float; gefolgt von einem optionalen Leerzeichen) abrufen und nur diese drucken.

Beispiel:

foo bar <foo> bla 1 2 3.4

Sollte drucken:

2 3.4

Bisher habe ich folgendes:

sed -n  's/\([0-9][0-9]*[\ \t][0-9.]*[\ \t]*$\)/replacement/p' 

wird mir geben

foo bar <foo> bla 1 replacement

Wenn ich jedoch versuche, es durch Gruppe 1 zu ersetzen, wird die gesamte Zeile gedruckt.

sed -n  's/\([0-9][0-9]*[\ \t][0-9.]*[\ \t]*$\)/\1/p' 

Wie kann ich nur den Abschnitt der Zeile drucken, der dem regulären Ausdruck in der Gruppe entspricht?

mort
quelle

Antworten:

138

Passen Sie die gesamte Zeile an, fügen Sie also .*am Anfang Ihrer Regex ein hinzu. Dadurch wird die gesamte Zeile durch den Inhalt der Gruppe ersetzt

echo "foo bar <foo> bla 1 2 3.4" |
 sed -n  's/.*\([0-9][0-9]*[\ \t][0-9.]*[ \t]*$\)/\1/p'
2 3.4
iruvar
quelle
38
Ich musste die -rOption "--regexp-extended" hinzufügen, da sonst invalid reference \1 on der RHS-Fehler des Befehls s angezeigt wurde .
Daniel Sokolowski
15
@ DanielSokolowski Ich denke, Sie bekommen diesen Fehler, wenn Sie (und )anstelle von \(und verwenden \).
Daniel Darabos
3
.*Denken Sie auch daran, am Ende des regulären Ausdrucks hinzuzufügen , wenn sich die zu extrahierende Zeichenfolge nicht immer am Ende der Zeile befindet.
Teemu Leisti
3
Das wird bei mir nicht funktionieren, weil .*es gierig ist und sed keine nicht gierige hat.*?
sondra.kinsey
@ DanielDarabos Erwähne das nur (und )werde in Ubuntu 16.04 keinen Fehler auslösen. Ich denke, dieser Kommentar ist veraltet.
Li Haonan
72

grep ist das richtige Werkzeug zum Extrahieren.

Verwenden Sie Ihr Beispiel und Ihren regulären Ausdruck:

kent$  echo 'foo bar <foo> bla 1 2 3.4'|grep -o '[0-9][0-9]*[\ \t][0-9.]*[\ \t]*$'
2 3.4
Kent
quelle
12
ideal
grep -o portiert nicht auf Systeme, auf denen msysgit ausgeführt wird, sed jedoch.
Cchamberlain
In der von @jozxyqk verknüpften Frage finden Sie eine Antwort, bei der Look-Ahead und Look-Behind verwendet werden, um dies mit grep zu lösen.
Joachim Breitner
Sie können eine Gruppe aus einem Muster mit weitergeleiteten grep -oAufrufen extrahieren . stackoverflow.com/a/58314379/117471
Bruno Bronosky
12

Und für eine weitere Option würde ich mit awk gehen!

echo "foo bar <foo> bla 1 2 3.4" | awk '{ print $(NF-1), $NF; }'

Dadurch wird die Eingabe (ich verwende hier STDIN, aber Ihre Eingabe könnte leicht eine Datei sein) auf Leerzeichen aufgeteilt und dann das vorletzte Feld und dann das letzte Feld ausgedruckt. Das$NF Variablen enthalten die Anzahl der Felder, die nach dem Auflösen von Leerzeichen gefunden wurden.

Dies hat den Vorteil, dass es keine Rolle spielt, ob sich das, was den letzten beiden Feldern vorausgeht, ändert, solange Sie nur die letzten beiden Felder möchten, funktioniert es weiterhin.

Chooban
quelle
3

Der Befehl cut ist genau für diese Situation ausgelegt. Es wird auf jedem Trennzeichen "geschnitten" und dann können Sie angeben, welche Chunks ausgegeben werden sollen.

Zum Beispiel: echo "foo bar <foo> bla 1 2 3.4" | cut -d " " -f 6-7

Wird zur Ausgabe von: 2 3.4

-d setzt das Trennzeichen

-f wählt den Bereich der auszugebenden 'Felder' aus. In diesem Fall ist dies der 6. bis 7. Block der ursprünglichen Zeichenfolge. Sie können den Bereich auch als Liste angeben, z 6,7.

carlin.scott
quelle
Um nur bestimmte Spalten zu drucken, awk '{ print $2" "$6 }'
leiten Sie
@nurettin Ich denke, dein Kommentar könnte für eine der awk-Antworten gedacht sein.
carlin.scott
Ich habe versucht zu schneiden, als ich diese Seite besuchte und erkannte, dass es Einschränkungen gibt, und entschied mich, stattdessen eine allgemeinere Version in awk als Kommentar zu schreiben, um die Qualität dieses Beitrags zu verbessern.
Nurettin
1
Ja, ich denke, das gehört zu einer anderen Antwort, die awk betrifft. Der Befehl cut, um das zu tun, was Sie geschrieben haben, lautet:cut -d " " -f 2,6
carlin.scott
ah, das wusste ich nicht, ich dachte du könntest nur Bereiche geben. Dank dafür.
Nurettin
2

Ich stimme @kent zu, dass dies gut geeignet ist grep -o. Wenn Sie eine Gruppe innerhalb eines Musters extrahieren müssen, können Sie dies mit einem zweiten Grep tun.

# To extract \1 from /xx([0-9]+)yy/
$ echo "aa678bb xx123yy xx4yy aa42 aa9bb" | grep -Eo 'xx[0-9]+yy' | grep -Eo '[0-9]+'
123
4

# To extract \1 from /a([0-9]+)b/
$ echo "aa678bb xx123yy xx4yy aa42 aa9bb" | grep -Eo 'a[0-9]+b' | grep -Eo '[0-9]+'
678
9
Bruno Bronosky
quelle