sed: wypisuje tylko pasującą grupę

136

Chcę pobrać dwie ostatnie liczby (jedną int, jedną zmiennoprzecinkową; po której następuje opcjonalna spacja) i wypisać tylko je.

Przykład:

foo bar <foo> bla 1 2 3.4

Powinien wydrukować:

2 3.4

Jak dotąd mam:

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

da mi

foo bar <foo> bla 1 replacement

Jeśli jednak spróbuję zastąpić ją grupą 1, drukowany jest cały wiersz.

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

Jak wydrukować tylko tę sekcję wiersza, która pasuje do wyrażenia regularnego w grupie?

mort
źródło
Pytanie dotyczy czegoś więcej niż tylko drukowania. Osoba z odpowiednimi uprawnieniami powinna zmodyfikować to pytanie.
Richard Jessop

Odpowiedzi:

145

Dopasuj całą linię, więc dodaj .*na początku swojego wyrażenia regularnego. Powoduje to zastąpienie całej linii zawartością grupy

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
źródło
40
Musiałem dodać -ropcję lub `--regexp-extended`, w przeciwnym razie otrzymywałem błąd invalid reference \1 on 'polecenia RHS'.
Daniel Sokolowski
15
@DanielSokolowski Myślę, że otrzymujesz ten błąd, jeśli używasz (i )zamiast \(i \).
Daniel Darabos
3
Pamiętaj również, aby dodać .*na końcu wyrażenia regularnego, jeśli ciąg, który chcesz wyodrębnić, nie zawsze znajduje się na końcu wiersza.
Teemu Leisti
4
To nie zadziała, ponieważ .*jest chciwy, a sed nie ma .*?
niechciwego
@DanielDarabos Wspomnij tylko o tym (i )nie spowoduje to błędu w Ubuntu 16.04. Myślę więc, że ten komentarz jest nieaktualny.
Li haonan,
74

grep to właściwe narzędzie do wyodrębniania.

używając swojego przykładu i wyrażenia regularnego:

kent$  echo 'foo bar <foo> bla 1 2 3.4'|grep -o '[0-9][0-9]*[\ \t][0-9.]*[\ \t]*$'
2 3.4
Kent
źródło
13
świetne dla całej grupy, chociaż sed jest potrzebny dla poszczególnych grup
jozxyqk
grep -o nie przenosi portów na systemach z uruchomionym msysgitem, ale sed to robi.
cchamberlain
Zobacz pytanie, do którego prowadzi @jozxyqk, aby uzyskać odpowiedź, która wykorzystuje antycypowanie i patrzenie w tył, aby rozwiązać ten problem za pomocą grep.
Joachim Breitner
Możesz wyodrębnić grupę ze wzoru z grep -opołączeniami potokowymi . stackoverflow.com/a/58314379/117471
Bruno Bronosky
12

A jeśli chodzi o jeszcze jedną opcję, wybrałbym awk!

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

Spowoduje to podzielenie danych wejściowych (używam tutaj STDIN, ale dane wejściowe mogą z łatwością być plikiem) na spacje, a następnie wydrukuje przedostatnie pole, a następnie ostatnie pole. Te $NFzmienne przytrzymaj liczba pól znaleziono po eksplozji na przestrzeniach.

Zaletą tego jest to, że nie ma znaczenia, czy zmieni się to, co poprzedza dwa ostatnie pola, o ile tylko chcesz, aby ostatnie dwa pola nadal działały.

chooban
źródło
4

Polecenie cięcia jest przeznaczone właśnie do tej sytuacji. Spowoduje to „wycięcie” dowolnego separatora, a następnie można określić, które fragmenty mają zostać wyprowadzone.

Na przykład: echo "foo bar <foo> bla 1 2 3.4" | cut -d " " -f 6-7

Daje wynik: 2 3.4

-d ustawia separator

-f wybiera zakres 'pól' do wyprowadzenia, w tym przypadku jest to fragment od 6 do 7 oryginalnego ciągu. Możesz również określić zakres w postaci listy, na przykład 6,7.

carlin.scott
źródło
Aby wydrukować tylko niektóre kolumny, awk '{ print $2" "$6 }'
przejdź
@nurettin Myślę, że twój komentarz mógł dotyczyć jednej z odpowiedzi awk.
carlin.scott
Próbowałem wyciąć, kiedy odwiedziłem tę stronę i zdałem sobie sprawę z jego ograniczeń i postanowiłem napisać bardziej uogólnioną wersję w awk jako komentarz, aby poprawić jakość tego postu.
nurettin
1
Tak, myślę, że należy to do innej odpowiedzi dotyczącej awk. Polecenie cięcia umożliwiające wykonanie tego, co napisałeś, to:cut -d " " -f 2,6
carlin.scott
ach, nie wiedziałem tego, myślałem, że możesz podać tylko zakresy. Dziękuję za to.
nurettin
2

Zgadzam się z @kent, że dobrze się do tego nadaje grep -o. Jeśli chcesz wyodrębnić grupę we wzorcu, możesz to zrobić za pomocą drugiego grepa.

# 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
źródło