AWK: Yakalanan gruba çizgi deseninden erişin


229

Bir awk komutum varsa

pattern { ... }

ve desen bir yakalama grubu kullanıyorsa, blokta bu şekilde yakalanan dizeye nasıl erişebilirim?



Bazen (basit durumlarda) alan ayırıcısını ( FS) ayarlamak ve a ile eşleştirmek istediklerinizi seçmek mümkündür $field. Girişin önceden biçimlendirilmesi de yardımcı olabilir.
Krzysztof Jabłoński

1
Bir yoktur iyi bir yanıt yinelenen soru üzerine.
Samuel Edwin Ward

2
Samuel Edwin Ward: Bu da güzel bir cevap! Ama aynı zamanda gerektirir gawk(kullandığından beri gensub).
rampion

Yanıtlar:


176

Bu aşağı doğru bir yürüyüş şeridiydi ...

Awk'yi uzun zaman önce perl ile değiştirdim.

Görünüşe göre AWK düzenli ifade motoru gruplarını yakalamıyor.

şöyle bir şey kullanmayı düşünebilirsiniz:

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

-n bayrağı, perl'in awk gibi her satır üzerinde dönmesine neden olur.


3
Görünüşe göre birisi aynı fikirde değil. Bu web sayfası 2005'ten alınmıştır: tek-tips.com/faqs.cfm?fid=5674 Eşleşen grupları awk olarak yeniden kullanamayacağınızı onaylar.
Peter Tillemans

3
Hemen hemen tüm kullanım durumları için awk yerine 'perl -n -p -e ...' i tercih ederim, çünkü daha esnek, daha güçlü ve bence daha akılcı bir sözdizimi var.
Peter Tillemans

15
gawk! = awk. Bunlar farklı araçlardır ve gawkçoğu yerde varsayılan olarak kullanılamaz.
Oli

6
OP özellikle garip bir çözüm istedi, bu yüzden bunun bir cevap olduğunu düşünmüyorum.
Joppe

6
@Çözüm yoksa awk çözümü veremezsiniz. 3. satırda AWK'nın yakalama gruplarını desteklemediğini ve OP'nin bu cevabın kabul edildiği için takdir ettiği bir alternatif verdim. Bu soruya nasıl cevap verebilirim?
Peter Tillemans

335

Gawk ile, matchparantez içindeki grupları yakalamak için bu işlevi kullanabilirsiniz .

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

misal:

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

çıktılar cd.

Söz konusu özelliği uygulayan gawk kullanımına dikkat edin.

Taşınabilir bir alternatif için match()ve ile benzer sonuçlar elde edebilirsiniz substr.

misal:

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

çıktılar cd.


4
Evet, gxxx varyantlarının ek GNU iyiliği ve gücü vardır.
Peter Tillemans

BusyBox awk de çalışır.
MrMas

32

Bu her zaman ihtiyacım olan bir şey, bu yüzden bunun için bir bash işlevi yarattım. Glen Jackson'ın cevabına dayanıyor.

Tanım

Bunu .bash_profile vb. Öğenize ekleyin.

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

kullanım

Dosyadaki her satır için normal ifadeyi yakalama

$ cat filename | regex '.*'

Dosyadaki her satır için 1. normal ifade yakalama grubunu yakalayın

$ cat filename | regex '(.*)' 1

2
Kullanmaktan farkı grep -onedir?
bfontaine

@bfontaine grep -oYakalanan gruplar çıktılanabilir mi ?
Olle Härstedt

1
@ OlleHärstedt Hayır olamazdı. Yalnızca yakalama gruplarınız olmadığında kullanım durumunuzu kapsar. Bu durumda zincirlerle çirkinleşir grep -o.
bfontaine

15

GNU awk'yi kullanabilirsiniz:

$ 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/

12
+1. Ayrıca, herhangi bir awk ile:awk 'match($0, /.*(http.*?)\$/) { print substr($0,RSTART,RLENGTH) }'
Ed Morton

5
Budur Glenn Jackman cevabı ne diyor oldukça fazla.
rampion

1
Ed Morton: söyleyebileceğim üst düzey bir cevabı hak ediyor. edit: uhm ... bu RewriteRule (.*) http://www.mysite.net/$benim için yazdırır , hangi alt grup daha fazla.
rampion


4

Vanilya awk'de yakalamayı uzantılar olmadan da simüle edebilirsiniz. Ama sezgisel değil:

adım 1. dizenizde görünmeyen bazı karakterlerle eşleşmeleri çevrelemek için gensub kullanın. adim 2. Karaktere karşı bölme kullanın. adim 3. Bölünmüş dizideki diğer her öğe yakalama grubunuzdur.

$ echo 'ab cb ad' | awk '{bölünmüş (gensub (/ a ./, SUBSEP "&" SUBSEP, "g", 0 $), cap, SUBSEP); baskı kapağı [2] "|" kapak [4]; }'
ab | reklam

3
Bunun belirli gensubbir gawkişlev olduğundan neredeyse eminim . awk --version; -?) Yazarsanız awk'nizden ne alırsınız ? Hepinize iyi şanslar.
shellter

6
Gensub'un gawk-ism olduğundan tamamen eminim, ancak BusyBox awk'de de var. Bu cevap gsub kullanılarak da uygulanabilir:echo 'ab cb ad' | awk '{gsub(/a./,SUBSEP"&"SUBSEP);split($0,cap,SUBSEP);print cap[2]"|"cap[4]}'
dubiousjim

3
gensub () bir gawk uzantısıdır, gawk'ın kılavuzu açıkça söylüyor. Diğer awk varyantları da uygulayabilir, ancak yine de POSIX değildir. Gawk --posix '{gsub (...)}' deneyin ve şikayet edecek
MestreLion

2
@MestreLion, yani şikayet edecek gawk --posix '{gensub(...)}'.
dubiousjim

1
POSIX awkgensub işlevine sahip olmanız konusunda yanlış olmanıza rağmen , örneğin çok sınırlı bir senaryoya uygulandı: tüm desen gruplandırılmış, key=(value)sadece valueparçaları çıkarmak istediğimde hepsi gibi bir şeyle eşleşemiyor .
Miyav

2

Peter Tillemans'ın cevabını saran bir bash fonksiyonu bulmakta biraz zorlandım, ama işte burada buldum:

işlev normal ifade {perl -n -e "/ $ 1 / && printf \"% s \ n \ "," '$ 1'}

Ben "ms" yazdırılmasını istemiyorum, çünkü bu aşağıdaki düzenli ifade argümanı için opsb awk tabanlı bash işlevinden daha iyi çalıştı bulundu.

'([0-9]*)ms$'

Bu çözümü tercih ederim, çünkü grubun yakalamayı sınırlayan kısımlarını da görmezden gelirsiniz. Ancak, birisi bunun nasıl çalıştığını açıklayabilir mi? Bu perl sözdiziminin BASH'de düzgün çalışmasını sağlayamıyorum, çünkü çok iyi anlamıyorum - özellikle etraftaki çift / tek tırnak işaretleri$1
Demis

Daha önce ya da o zamandan beri yaptığım bir şey değil, ama ne yaptığını geriye bakmak iki dizeyi birleştiriyor, ilk dize çift tırnak içinde (bu ilk dize ters eğik çizgiyle kaçan gömülü çift tırnaklar içeriyor) ve ikinci dize tek tırnak içinde . Daha sonra bu birleştirmenin sonucu perl -e'ye argüman olarak sağlanır. Ayrıca, ilk 1 $ 'ın (çift tırnak içindeki) işlevin ilk argümanıyla değiştirildiğini, ikinci 1 $' ın (tek tırnak içindeki) dokunulmadan bırakıldığını bilmeniz gerekir. Bu örneğe
wytten

Anlıyorum, şimdi biraz daha mantıklı. Yani relx eşleştirme / grup yakalama tanımı perl komutunda nerede? Yazdığınızı görüyorum '([0-9]*)ms$'- bu bir argüman olarak mı (ve dize başka bir argüman) mı? Ve çıkış çıktısı perl -ebash'ın printfkomutuna yerleştiriliyor , yerine %s, bu doğru mu? Teşekkürler, bunu kullanmayı umuyorum.
Demis

1
Regex bash işlevine tek bağımsız değişken olarak tek tırnak içine alınmış normal bir ifade iletirsiniz. Örnek
wytten
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.