grupy przechwytywania sed nie działają

27

Mam ciąg formatu [0-9]+\.[0-9]+\.[0-9]. Muszę wyodrębnić pierwszą, drugą i trzecią liczbę osobno. Jak rozumiem, grupy przechwytywania powinny mieć taką możliwość. Powinienem być w stanie użyć sed "s/\([0-9]*\)/\1/gpierwszego numeru, sed "s/\([0-9]*\)/\2/gdrugiego numeru i sed "s/\([0-9]*\)/\3/gtrzeciego numeru. Jednak w każdym przypadku otrzymuję cały ciąg. Dlaczego to się dzieje?

Melab
źródło
6
Grupy przechwytywania przechwytują całą grupę ... a nie poszczególne elementy w grupie. Potrzebujesz czegoś, jak 's/\([0-9]\)\([0-9]\)\([0-9]\).*/\1\2\3/'uchwycić poszczególne liczby.
Munir

Odpowiedzi:

45

Nie możemy dać ci pełnej odpowiedzi bez przykładu twojego wkładu, ale mogę powiedzieć, że twoje rozumienie grup przechwytywania jest błędne. Nie używasz ich sekwencyjnie, odnoszą się one tylko do wyrażenia regularnego po lewej stronie tego samego operatora podstawienia. Jeśli na przykład przechwycisz, /(foo)(bar)(baz)/wtedy foobędzie \1, barbędzie \2i bazbędzie \3. Nie możesz tego zrobić s/(foo)/\1/; s/(bar)/\2/, ponieważ w drugim s///połączeniu jest tylko jedna grupa przechwycona, więc \2nie zostanie zdefiniowana.

Aby przechwycić trzy grupy cyfr, musisz zrobić:

sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\1 : \2 : \3/'

Lub, bardziej czytelny:

sed -E 's/([0-9]*)\.([0-9]*)\.([0-9]*)/\1 : \2 : \3/'
terdon
źródło
1
Jaka jest korzyść z unikania nawiasów w pierwszym przykładzie?
Josh M.
2
@JoshM. musisz uciec od nich, aby mogły zostać wykorzystane do przechwytywania wzorów. Normalnie /(foo)/w sed będzie pasować dosłowny (znak, a foonastępnie literał ). Jeśli chcesz przechwycić grupę, musisz uciec z nawiasów lub skorzystać z -Eopcji.
terdon
Prawie zawsze używam -rflagi, więc zakładam, że dlatego na to nie wpadłem.
Josh M.,
1
@JoshM. tak, -rflaga to zrobi, ale nie jest przenośna. GNU sed obsługuje to, ale wiele innych nie. -EJest bardziej uniwersalna.
terdon
9

Przykład:

$ echo "123.456.78" |sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\1/'
123

$ echo "123.456.78" |sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\2/'
456

$ echo "123.456.78" |sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\3/'
78

Lub wszystkie razem:

$ echo "123.456.78" |sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\1 : \2 : \3/'
123 : 456 : 78
jai_s
źródło
2

Użyj Sed z opcją -r, --regexp-Extended, aby uniknąć wszystkich nawiasów, których nie można uniknąć

echo "1234.567.89" | sed -r 's/([0-9]+)\.([0-9]+)\.([0-9]+)/\1, \2, \3/' 
1234, 567, 89    #output
Surya
źródło