AWK: Akses grup yang diambil dari pola garis

229

Jika saya memiliki perintah awk

pattern { ... }

dan pola menggunakan grup penangkap, bagaimana saya bisa mengakses string yang ditangkap di blok?

rampion
sumber
Terkadang (dalam kasus sederhana) dimungkinkan untuk menyesuaikan pemisah bidang ( FS) dan memilih yang ingin dicocokkan dengan a $field. Memformat ulang input juga dapat membantu.
Krzysztof Jabłoński
1
Ada jawaban yang lebih baik pada pertanyaan duplikat.
Samuel Edwin Ward
2
Samuel Edwin Ward: Itu jawaban yang bagus juga! Tetapi juga membutuhkan gawk(karena menggunakan gensub).
rampion

Jawaban:

176

Itu berjalan-jalan di jalur memori ...

Saya menggantikan awk dengan perl sejak lama.

Rupanya mesin ekspresi reguler AWK tidak menangkap kelompoknya.

Anda mungkin mempertimbangkan untuk menggunakan sesuatu seperti:

perl -n -e'/test(\d+)/ && print $1'

flag -n menyebabkan perl untuk mengulangi setiap baris seperti awk.

Peter Tillemans
sumber
3
Rupanya seseorang tidak setuju. Halaman web ini dari 2005: tek-tips.com/faqs.cfm?fid=5674 Ini menegaskan bahwa Anda tidak dapat menggunakan kembali grup yang cocok di awk.
Peter Tillemans
3
Saya lebih suka 'perl -n -p -e ...' daripada awk untuk hampir semua kasus penggunaan, karena lebih fleksibel, lebih kuat dan memiliki sintaks yang lebih waras menurut saya.
Peter Tillemans
15
gawk! = awk. Mereka adalah alat yang berbeda dan gawktidak tersedia secara default di sebagian besar tempat.
Oli
6
OP secara khusus meminta solusi awk, jadi saya rasa ini bukan jawaban.
Joppe
6
@Joppe Anda tidak dapat memberikan solusi awk jika tidak ada solusi. Di baris 3 saya jelaskan bahwa AWK tidak mendukung kelompok penangkap dan saya memberikan alternatif, yang tampaknya dihargai OP karena jawaban ini diterima. Bagaimana saya bisa menjawab pertanyaan ini dengan lebih baik?
Peter Tillemans
335

Dengan gawk, Anda dapat menggunakan matchfungsi ini untuk menangkap grup yang diurung.

gawk 'match($0, pattern, ary) {print ary[1]}' 

contoh:

echo "abcdef" | gawk 'match($0, /b(.*)e/, a) {print a[1]}' 

output cd.

Perhatikan penggunaan spesifik dari gawk yang mengimplementasikan fitur yang dimaksud.

Untuk alternatif portabel Anda dapat mencapai hasil yang serupa dengan match()dan substr.

contoh:

echo "abcdef" | awk 'match($0, /b[^e]*/) {print substr($0, RSTART+1, RLENGTH-1)}'

output cd.

glenn jackman
sumber
4
Ya, varian gxxx memiliki banyak kebaikan dan kekuatan GNU tambahan.
Peter Tillemans
Bekerja di BusyBox awk juga.
MrMas
32

Ini adalah sesuatu yang saya butuhkan sepanjang waktu jadi saya membuat fungsi bash untuknya. Ini berdasarkan jawaban glenn jackman.

Definisi

Tambahkan ini ke .bash_profile Anda dll.

function regex { gawk 'match($0,/'$1'/, ary) {print ary['${2:-'0'}']}'; }

Pemakaian

Abadikan regex untuk setiap baris dalam file

$ cat filename | regex '.*'

Capture 1st regex capture group untuk setiap baris dalam file

$ cat filename | regex '(.*)' 1
opsb
sumber
2
Apa bedanya dengan menggunakan grep -o?
bfontaine
@ bfontaine Bisakah grep -omenampilkan grup yang ditangkap?
Olle Härstedt
1
@ OlleHärstedt Tidak, tidak bisa. Ini hanya mencakup kasus penggunaan Anda ketika Anda tidak memiliki kelompok tangkapan. Dalam hal itu menjadi jelek dengan rantai grep -o.
bfontaine
15

Anda dapat menggunakan GNU awk:

$ cat hta
RewriteCond %{HTTP_HOST} !^www\.mysite\.net$
RewriteRule (.*) http://www.mysite.net/$1 [R=301,L]

$ gawk 'match($0, /.*(http.*?)\$/, m) { print m[1]; }' < hta
http://www.mysite.net/
Isvara
sumber
12
+1. Juga, dengan awk:awk 'match($0, /.*(http.*?)\$/) { print substr($0,RSTART,RLENGTH) }'
Ed Morton
5
Itulah yang dikatakan oleh jawaban glenn jackman , cukup banyak.
rampion
1
Ed Morton: itu pantas saya jawab. edit: uhm ... yang mencetak RewriteRule (.*) http://www.mysite.net/$untuk saya, yang lebih dari subkelompok.
rampion
4

Anda dapat mensimulasikan menangkap vanila awk juga, tanpa ekstensi. Itu tidak intuitif:

langkah 1. gunakan gensub untuk mengelilingi kecocokan dengan beberapa karakter yang tidak muncul di string Anda. langkah 2. Gunakan split terhadap karakter. langkah 3. Setiap elemen lain dalam array yang dibelah adalah grup tangkapan Anda.

$ echo 'ab cb ad' | awk '{split (gensub (/ a ./, SUBSEP "&" SUBSEP, "g", $ 0), cap, SUBSEP); tutup cetak [2] "|" cap [4]; } '
ab | iklan
ydrol
sumber
3
Saya hampir yakin itu gensubadalah gawkfungsi spesifik. Apa yang Anda dapatkan dari pekerjaan Anda jika Anda mengetik awk --version; -?). Semoga beruntung untuk semua.
shellter
6
Saya sepenuhnya yakin bahwa gensub adalah gawk-isme, meskipun BusyBox awk juga memilikinya. Jawaban ini juga dapat diimplementasikan menggunakan gsub, meskipun:echo 'ab cb ad' | awk '{gsub(/a./,SUBSEP"&"SUBSEP);split($0,cap,SUBSEP);print cap[2]"|"cap[4]}'
dubiousjim
3
gensub () adalah ekstensi gawk, manual gawk jelas mengatakannya. Varian awk lain mungkin juga mengimplementasikannya, tetapi masih belum POSIX. Coba gawk --posix '{gsub (...)}' dan itu akan mengeluh
MestreLion
2
@MestreLion, maksud Anda akan mengadu gawk --posix '{gensub(...)}'.
dubiousjim
1
Meskipun Anda salah tentang POSIX awk yang memiliki gensubfungsi, contoh Anda diterapkan pada skenario yang sangat terbatas: seluruh pola dikelompokkan, itu tidak dapat cocok dengan sesuatu seperti semua key=(value)ketika saya ingin mengekstrak hanya valuebagian - bagiannya.
Meow
2

Saya sedikit berjuang dengan menghasilkan fungsi bash yang membungkus jawaban Peter Tillemans tetapi inilah yang saya kemukakan:

function regex {perl -n -e "/ $ 1 / && printf \"% s \ n \ "," '$ 1'}

Saya menemukan ini bekerja lebih baik daripada fungsi bash berbasis aws opsb untuk argumen ekspresi reguler berikut, karena saya tidak ingin "ms" dicetak.

'([0-9]*)ms$'
wytten
sumber
Saya lebih suka solusi ini, karena Anda dapat melihat bagian-bagian kelompok yang membatasi penangkapan, sementara juga menghilangkannya. Namun, dapatkah seseorang menjelaskan cara kerjanya? Saya tidak bisa mendapatkan sintaks perl ini berfungsi dengan baik di BASH, karena saya tidak memahaminya dengan baik - terutama tanda kutip ganda / tunggal di sekitar$1
Demis
Ini bukan sesuatu yang telah saya lakukan sebelum atau sesudahnya, tetapi melihat ke belakang apa yang dilakukannya adalah merangkai dua string, string pertama berada dalam tanda kutip ganda (string pertama ini berisi tanda kutip ganda tertanam yang lolos dengan garis miring terbalik) dan string kedua berada dalam tanda kutip tunggal . Kemudian hasil dari rangkaian itu diberikan sebagai argumen untuk perl -e. Anda juga perlu tahu bahwa $ 1 pertama (yang dalam tanda kutip ganda) diganti dengan argumen pertama ke fungsi, sedangkan $ 1 kedua (yang dalam tanda kutip tunggal) dibiarkan tidak tersentuh. Lihat contoh ini
wytten
Saya mengerti, itu lebih masuk akal sekarang. Jadi di mana di perintah perl definisi regex match / group capture? Saya melihat Anda menulis '([0-9]*)ms$'- apakah itu disediakan sebagai argumen (dan string argumen lain)? Dan output dari perl -esedang dimasukkan ke dalam printfperintah bash kemudian, untuk menggantikannya %s, apakah itu benar? Terima kasih, saya berharap bisa menggunakan ini.
Demis
1
Anda memberikan ekspresi reguler yang dilampirkan dalam tanda kutip tunggal sebagai satu-satunya argumen untuk fungsi bash regex. Contoh
wytten