Jak wyodrębnić tekst z ciągu za pomocą seda?

98

Mój przykładowy ciąg jest następujący:

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

Teraz z powyższego ciągu chcę wyodrębnić 02G05. W tym celu wypróbowałem następujące wyrażenie regularne z sed

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

Ale powyższe polecenie nic nie drukuje i uważam, że nie jest w stanie dopasować niczego do wzorca, który dostarczyłem sedowi.

Więc moje pytanie brzmi: co robię źle i jak to poprawić.

Kiedy próbuję powyższego ciągu i wzoru w Pythonie, otrzymuję wynik

>>> re.findall(r'\d+G\d+',st)
['02G05']
>>>
RanRag
źródło
6
Python zdecydowanie nie jest sed. Ich smaki regex są zupełnie inne.
tripleee

Odpowiedzi:

96

Wzorzec \dmoże nie być obsługiwany przez sed. Spróbuj [0-9]lub [[:digit:]]zamiast tego.

Aby wydrukować tylko rzeczywiste dopasowanie (a nie całą pasującą linię), użyj podstawienia.

sed -n 's/.*\([0-9][0-9]*G[0-9][0-9]*\).*/\1/p'
tripleee
źródło
6
Dzięki, działało dobrze. Ale mam pytanie, dlaczego .*jest to konieczne z twoim wyrażeniem regularnym, ponieważ kiedy próbuję sed -n 's/\([0-9]\+G[0-9]\+\)/\1/p', po prostu drukuje cały wiersz.
RanRag,
7
Dlatego, prawda? Zastąp wszystko, co pojawi się przed i po dopasowaniu, na norhing, a następnie wydrukuj całą linię.
tripleee
1
@tripleee To tylko 2G05nie drukuje 02G05. Wyrażenie, które działa, to's/.*\([0-9][0-9]G[0-9][0-9]*\).*/\1/p'
Kshitiz Sharma
1
To sztywno koduje go na dokładnie dwie cyfry. Coś takiego sed -n 's/\(.*[^0-9]\)\?\([0-9][0-9]*G[0-9][0-9]*\).*/\2/p'byłoby bardziej ogólne. (Zakładam swoje sedwsporniki \?do zera lub jednego wystąpienia.)
tripleee
Zobacz także stackoverflow.com/a/48898886/874188 na sposób wymiany różnych innych wspólnych ucieka jak Perl \w, \sitp
tripleee
102

A co powiesz na używanie grep -E?

echo "This is 02G05 a test string 20-Jul-2012" | grep -Eo '[0-9]+G[0-9]+'
mVChr
źródło
3
+1 Jest to prostsze i będzie również poprawnie obsługiwać przypadek wielu dopasowań w tej samej linii. sedMożna by opracować złożony scenariusz dla tego przypadku, ale po co się tym przejmować?
tripleee
egrepużywa rozszerzonego wyrażenia regularnego sedi grepużywa standardowego wyrażenia regularnego egreplub grep -elub sed -Erozszerzonego wyrażenia regularnego, a kod Pythona w pytaniu używa PCRE, (typowe wyrażenie regularne perla) GNU grep może używać PCRE z -Popcją.
Felipe Buccioni
@FelipeBuccioni właściwie to powinno być egreplub grep -Elubsed -r
SensorSmith
Dla pojedynczego (pierwszego) dopasowania dodaj `| głowa -1` (bez grawitacji), zgodnie z tą odpowiedzią na inne pytanie.
SensorSmith
1
grepmusi -m 1się zatrzymać po pierwszym meczu.
tripleee
5

sednie rozpoznaje \d, użyj [[:digit:]]zamiast tego. Będziesz także musiał uciec +lub użyć -rprzełącznika ( -Ena OS X).

Zauważ, że [0-9]działa to również w przypadku cyfr arabsko-hinduskich.

Wstrzymano do odwołania.
źródło
Próbowałem sed -n '/[0-9]\+G[0-9]\+/p'. Teraz po prostu drukuje cały ciąg
RanRag,
@Noob: Będziesz musiał użyć zastępowania, aby wykluczyć części, których nie chcesz drukować .
Wstrzymano do odwołania.
5

Spróbuj tego zamiast tego:

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

Ale zwróć uwagę, że jeśli w jednym wierszu znajdują się dwa wzory, drukuje drugi.

Zsolt Botykai
źródło
Lub bardziej ogólnie ostatni, jeśli jest wiele dopasowań.
tripleee
0

Spróbuj użyć rextract . Pozwoli ci to wyodrębnić tekst za pomocą wyrażenia regularnego i sformatować go.

Przykład:

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

2G05
Tim Savannah
źródło
Jeśli używa standardowego wyrażenia regularnego, nawiasy kwadratowe wokół \dsą całkowicie zbędne.
tripleee