Ketika regexp berisi grup, mungkin ada lebih dari satu cara untuk mencocokkan string terhadapnya: regexps dengan grup bersifat mendua. Sebagai contoh, perhatikan regexp ^.*\([0-9][0-9]*\)$
dan string a12
. Ada dua kemungkinan:
- Pertandingan
a
melawan .*
dan 2
melawan [0-9]*
; 1
cocok dengan [0-9]
.
- Cocok
a1
melawan .*
dan string kosong melawan [0-9]*
; 2
cocok dengan [0-9]
.
Sed, seperti semua alat regexp lainnya di luar sana, menerapkan aturan kecocokan terlama yang paling awal: pertama-tama mencoba untuk mencocokkan bagian panjang variabel pertama terhadap string yang selama mungkin. Jika menemukan cara untuk mencocokkan sisa string dengan sisa regexp, baik-baik saja. Jika tidak, sed mencoba pencocokan terpanjang berikutnya untuk bagian panjang variabel pertama dan mencoba lagi.
Di sini, pertandingan dengan string terlama pertama adalah a1
melawan .*
, jadi grup hanya cocok 2
. Jika Anda ingin grup memulai lebih awal, beberapa mesin regexp memungkinkan Anda membuat yang .*
kurang serakah, tetapi tidak memiliki fitur seperti itu. Jadi, Anda perlu menghapus ambiguitas dengan beberapa jangkar tambahan. Tentukan bahwa pemimpin .*
tidak dapat diakhiri dengan digit, sehingga digit pertama grup adalah kecocokan pertama yang mungkin.
Jika kelompok angka tidak boleh berada di awal baris:
sed -n 's/^.*[^0-9]\([0-9][0-9]*\).*/\1/p'
Jika kelompok angka bisa di awal garis, dan sed Anda mendukung \?
operator untuk suku cadang opsional:
sed -n 's/^\(.*[^0-9]\)\?\([0-9][0-9]*\).*/\1/p'
Jika kelompok angka dapat berada di awal garis, tetap pada konstruksi regexp standar:
sed -n -e 's/^.*[^0-9]\([0-9][0-9]*\).*/\1/p' -e t -e 's/^\([0-9][0-9]*\).*/\1/p'
Ngomong-ngomong, aturan pertandingan terlama yang sama yang paling awal yang membuat [0-9]*
cocok dengan digit setelah yang pertama, bukan yang berikutnya .*
.
Perhatikan bahwa jika ada beberapa urutan angka pada satu baris, program Anda akan selalu mengekstrak urutan angka terakhir, sekali lagi karena aturan kecocokan terlama yang diterapkan pada inisial .*
. Jika Anda ingin mengekstrak urutan angka pertama, Anda perlu menentukan bahwa apa yang datang sebelumnya adalah urutan angka non-digit.
sed -n 's/^[^0-9]*\([0-9][0-9]*\).*$/\1/p'
Secara umum, untuk mengekstrak kecocokan pertama dari regexp, Anda perlu menghitung negasi dari regexp tersebut. Walaupun ini selalu memungkinkan secara teoritis, ukuran negasi tumbuh secara eksponensial dengan ukuran regexp yang Anda negasikan, jadi ini seringkali tidak praktis.
Pertimbangkan contoh Anda yang lain:
sed -n 's/.*\(CONFIG_[a-zA-Z0-9_]*\).*/\1/p'
Contoh ini sebenarnya menunjukkan masalah yang sama, tetapi Anda tidak melihatnya pada input biasa. Jika Anda memberinya makan hello CONFIG_FOO_CONFIG_BAR
, maka perintah di atas dicetak CONFIG_BAR
, tidak CONFIG_FOO_CONFIG_BAR
.
Ada cara untuk mencetak pertandingan pertama dengan sed, tetapi sedikit rumit:
sed -n -e 's/\(CONFIG_[a-zA-Z0-9_]*\).*/\n\1/' -e T -e 's/^.*\n//' -e p
(Dengan asumsi sed Anda mendukung \n
berarti baris baru di s
teks pengganti.) Ini berfungsi karena sed mencari pertandingan regexp yang paling awal, dan kami tidak mencoba untuk mencocokkan apa yang mendahului CONFIG_…
bit. Karena tidak ada baris baru di dalam baris, kita dapat menggunakannya sebagai penanda sementara. The T
perintah mengatakan menyerah jika sebelumnya s
perintah tidak cocok.
Ketika Anda tidak tahu bagaimana melakukan sesuatu dalam sed, beralihlah ke awk. Perintah berikut mencetak pencocokan terlama dari regexp:
awk 'match($0, /[0-9]+/) {print substr($0, RSTART, RLENGTH)}'
Dan jika Anda ingin menjaganya tetap sederhana, gunakan Perl.
perl -l -ne '/[0-9]+/ && print $&' # first match
perl -l -ne '/^.*([0-9]+)/ && print $1' # last match