Wie extrahiere ich Text aus einem String mit sed?

95

Meine Beispielzeichenfolge lautet wie folgt:

This is 02G05 a test string 20-Jul-2012

Nun möchte ich aus der obigen Zeichenfolge extrahieren 02G05. Dafür habe ich den folgenden Regex mit sed ausprobiert

$ echo "This is 02G05 a test string 20-Jul-2012" | sed -n '/\d+G\d+/p'

Aber der obige Befehl gibt nichts aus und der Grund, den ich glaube, ist, dass er nichts mit dem Muster vergleichen kann, das ich sed geliefert habe.

Meine Frage ist also, was ich hier falsch mache und wie ich es korrigieren kann.

Wenn ich die obige Zeichenfolge und das obige Muster mit Python ausprobiere, erhalte ich mein Ergebnis

>>> re.findall(r'\d+G\d+',st)
['02G05']
>>>
RanRag
quelle
6
Python ist definitiv nicht sed. Ihre Regex-Aromen sind sehr unterschiedlich.
Tripleee

Antworten:

90

Das Muster wird \dmöglicherweise von Ihrem nicht unterstützt sed. Versuchen Sie es [0-9]oder [[:digit:]]stattdessen.

Verwenden Sie eine Ersetzung, um nur die tatsächliche Übereinstimmung (nicht die gesamte übereinstimmende Zeile) zu drucken.

sed -n 's/.*\([0-9][0-9]*G[0-9][0-9]*\).*/\1/p'
Tripleee
quelle
6
Danke, es hat gut funktioniert. Aber ich habe eine Frage, warum .*dies bei Ihrer Regex notwendig ist, denn wenn ich es versuche sed -n 's/\([0-9]\+G[0-9]\+\)/\1/p', wird nur die gesamte Zeile gedruckt.
RanRag
7
Das ist der Grund, nicht wahr? Ersetzen Sie alles, was vor und nach dem Match kommt, durch nichts und drucken Sie dann die gesamte Zeile.
Tripleee
1
@tripleee Dies druckt nur 2G05nicht 02G05. Der Ausdruck, der funktioniert, ist's/.*\([0-9][0-9]G[0-9][0-9]*\).*/\1/p'
Kshitiz Sharma
1
Das codiert es genau auf zwei Ziffern. So etwas sed -n 's/\(.*[^0-9]\)\?\([0-9][0-9]*G[0-9][0-9]*\).*/\2/p'wäre allgemeiner. (Ich nehme an, Ihre sedUnterstützung \?für null oder ein Vorkommen.)
Tripleee
Siehe auch stackoverflow.com/a/48898886/874188 dafür , wie verschiedene andere gemeinsame ersetzen Perl entkommt wie \w, \setc.
tripleee
98

Wie wäre es mit grep -E?

echo "This is 02G05 a test string 20-Jul-2012" | grep -Eo '[0-9]+G[0-9]+'
mVChr
quelle
3
+1 Dies ist einfacher und behandelt auch den Fall mehrerer Übereinstimmungen in derselben Zeile korrekt. sedFür diesen Fall könnte ein komplexes Skript entwickelt werden, aber warum sollte man sich die Mühe machen?
Tripleee
egrepverwendet Extended Regexp sedund grepverwendet Standard Regexp egrepoder grep -eoder sed -EExtended Regexp, und der Python-Code in der Frage verwendet PCRE (Perl Common Regular Expression). GNU grep kann PCRE mit -POption verwenden.
Felipe Buccioni
@FelipeBuccioni eigentlich sollte das sein egrepoder grep -Eodersed -r
SensorSmith
Fügen Sie für eine einzelne (erste) Übereinstimmung `| hinzu Kopf -1` (ohne Backticks), gemäß dieser Antwort auf eine andere Frage.
SensorSmith
1
grepmuss -m 1nach dem ersten Spiel aufhören.
Tripleee
5

sederkennt nicht \d, verwenden Sie [[:digit:]]stattdessen. Sie müssen auch +den -rSchalter verlassen oder den Schalter verwenden ( -Eunter OS X).

Beachten Sie, dass dies [0-9]auch für arabisch-hinduistische Ziffern funktioniert.

Bis auf weiteres angehalten.
quelle
Ich habe es versucht sed -n '/[0-9]\+G[0-9]\+/p'. Jetzt druckt es nur die ganze Zeichenfolge
RanRag
@Noob: Sie müssen die Teile ersetzen , die Sie nicht drucken möchten .
Bis auf weiteres angehalten.
5

Versuchen Sie stattdessen Folgendes:

echo "This is 02G05 a test string 20-Jul-2012" | sed 's/.* \([0-9]\+G[0-9]\+\) .*/\1/'

Beachten Sie jedoch, dass bei einer Zeile mit zwei Mustern das zweite gedruckt wird.

Zsolt Botykai
quelle
Oder allgemeiner die letzte, wenn es mehrere Übereinstimmungen gibt.
Tripleee
0

Versuchen Sie es mit rextract . Sie können Text mit einem regulären Ausdruck extrahieren und neu formatieren.

Beispiel:

$ echo "This is 02G05 a test string 20-Jul-2012" | ./rextract '([\d]+G[\d]+)' '${1}'

2G05
Tim Savannah
quelle
Wenn hierfür Standard-Regex verwendet wird, sind die eckigen Klammern \dvöllig überflüssig.
Tripleee