Gdy wyrażenie regularne zawiera grupy, może istnieć więcej niż jeden sposób dopasowania ciągu do niego: wyrażenia regularne z grupami są niejednoznaczne. Na przykład rozważ wyrażenie regularne ^.*\([0-9][0-9]*\)$
i ciąg a12
. Istnieją dwie możliwości:
- Dopasuj
a
przeciw .*
i 2
przeciw [0-9]*
; 1
jest dopasowany przez [0-9]
.
- Dopasuj
a1
do .*
i pusty ciąg przeciw [0-9]*
; 2
jest dopasowany przez [0-9]
.
Sed, podobnie jak wszystkie inne narzędzia regexp, stosuje najwcześniejszą zasadę najdłuższego dopasowania: najpierw próbuje dopasować pierwszą część o zmiennej długości do łańcucha, który jest tak długi, jak to możliwe. Jeśli znajdzie sposób, aby dopasować resztę ciągu do reszty wyrażenia regularnego, dobrze. W przeciwnym razie sed próbuje następnego najdłuższego dopasowania dla pierwszej części o zmiennej długości i próbuje ponownie.
Tutaj dopasowanie do najdłuższego ciągu jest najpierw a1
przeciw .*
, więc grupa dopasowuje tylko 2
. Jeśli chcesz, aby grupa zaczęła wcześniej, niektóre silniki wyrażeń regularnych pozwalają ci być .*
mniej chciwym, ale sed nie ma takiej funkcji. Musisz więc usunąć niejednoznaczność za pomocą dodatkowej kotwicy. Określ, że wiodący .*
nie może kończyć się cyfrą, aby pierwsza cyfra w grupie była pierwszym możliwym dopasowaniem.
Jeśli grupa cyfr nie może znajdować się na początku wiersza:
sed -n 's/^.*[^0-9]\([0-9][0-9]*\).*/\1/p'
Jeśli grupa cyfr może znajdować się na początku wiersza, a Twój sed obsługuje \?
operatora dla opcjonalnych części:
sed -n 's/^\(.*[^0-9]\)\?\([0-9][0-9]*\).*/\1/p'
Jeśli grupa cyfr może znajdować się na początku wiersza, trzymaj się standardowych konstrukcji wyrażeń regularnych:
sed -n -e 's/^.*[^0-9]\([0-9][0-9]*\).*/\1/p' -e t -e 's/^\([0-9][0-9]*\).*/\1/p'
Nawiasem mówiąc, to ta sama najwcześniejsza najdłuższa reguła dopasowania, która powoduje [0-9]*
dopasowanie cyfr po pierwszej, a nie następnej .*
.
Zauważ, że jeśli w linii znajduje się wiele sekwencji cyfr, Twój program zawsze wyodrębni ostatnią sekwencję cyfr, ponownie ze względu na najwcześniejszą najdłuższą regułę dopasowania zastosowaną do początkowej .*
. Jeśli chcesz wyodrębnić pierwszą sekwencję cyfr, musisz określić, że to, co ma miejsce wcześniej, to sekwencja niecyfrowa.
sed -n 's/^[^0-9]*\([0-9][0-9]*\).*$/\1/p'
Mówiąc bardziej ogólnie, aby wyodrębnić pierwsze dopasowanie wyrażenia regularnego, musisz obliczyć negację tego wyrażenia regularnego. Chociaż zawsze jest to teoretycznie możliwe, rozmiar negacji rośnie wykładniczo wraz z wielkością wyrażenia regularnego, którego negujesz, więc często jest to niepraktyczne.
Rozważ swój inny przykład:
sed -n 's/.*\(CONFIG_[a-zA-Z0-9_]*\).*/\1/p'
Ten przykład faktycznie pokazuje ten sam problem, ale nie widać go na typowych danych wejściowych. Jeśli go nakarmisz hello CONFIG_FOO_CONFIG_BAR
, to powyższe polecenie zostanie wydrukowane CONFIG_BAR
, a nie CONFIG_FOO_CONFIG_BAR
.
Jest sposób na wydrukowanie pierwszego meczu za pomocą sed, ale jest to trochę trudne:
sed -n -e 's/\(CONFIG_[a-zA-Z0-9_]*\).*/\n\1/' -e T -e 's/^.*\n//' -e p
(Zakładając, że twój sed obsługuje \n
oznaczenie nowego wiersza w s
tekście zastępującym). Działa to, ponieważ sed szuka najwcześniejszego dopasowania wyrażenia regularnego, a my nie próbujemy dopasować tego, co poprzedza CONFIG_…
bit. Ponieważ w linii nie ma nowego wiersza, możemy go użyć jako znacznika tymczasowego. T
Komenda mówi zrezygnować jeśli poprzedzający s
komenda nie pasuje.
Kiedy nie możesz wymyślić, jak coś zrobić w sed, przejdź do awk. Następujące polecenie wypisuje najwcześniejsze dopasowanie wyrażenia regularnego:
awk 'match($0, /[0-9]+/) {print substr($0, RSTART, RLENGTH)}'
A jeśli masz ochotę to uprościć, użyj Perla.
perl -l -ne '/[0-9]+/ && print $&' # first match
perl -l -ne '/^.*([0-9]+)/ && print $1' # last match