AWK: เข้าถึงกลุ่มที่ถูกจับจากรูปแบบเส้น


229

หากฉันมีคำสั่ง awk

pattern { ... }

และรูปแบบใช้กลุ่มการจับภาพฉันจะเข้าถึงสตริงที่ถูกจับในบล็อกได้อย่างไร



บางครั้ง (ในกรณีที่เรียบง่าย) มันเป็นไปได้ที่จะปรับตัวคั่นฟิลด์ ( FS) $fieldและเลือกสิ่งที่ต้องการเพื่อให้ตรงกับ การฟอร์แมตอินพุตก็ช่วยได้เช่นกัน
Krzysztof Jabłoński

1
มีคำตอบที่ดีกว่าสำหรับคำถามที่ซ้ำกัน
ซามูเอลเอ็ดวินวอร์ด

2
Samuel Edwin Ward: นั่นเป็นคำตอบที่ดีเช่นกัน! แต่มันก็ยังต้องการgawk(เนื่องจากมันใช้gensub)
rampion

คำตอบ:


176

นั่นเป็นช่องทางเดินลงหน่วยความจำ ...

ฉันเปลี่ยน awk ด้วย perl เป็นเวลานานแล้ว

เห็นได้ชัดว่าเครื่องมือแสดงออกปกติ AWK ไม่จับกลุ่ม

คุณอาจลองใช้สิ่งต่อไปนี้:

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

แฟล็ก -n ทำให้ perl วนซ้ำทุกบรรทัดเหมือน awk


3
เห็นได้ชัดว่ามีคนไม่เห็นด้วย หน้าเว็บนี้มาจากปี 2005: tek-tips.com/faqs.cfm?fid=5674 ซึ่งยืนยันว่าคุณไม่สามารถนำกลุ่มที่ตรงกันกลับมาใช้ใหม่ได้ใน awk
Peter Tillemans

3
ฉันชอบ 'perl -n -p -e ... ' มากกว่า awk สำหรับกรณีการใช้งานเกือบทุกประเภทเนื่องจากมีความยืดหยุ่นมากขึ้นมีประสิทธิภาพมากกว่าและมีไวยากรณ์ saner ในความคิดของฉัน
Peter Tillemans

15
gawk! awk= เป็นเครื่องมือที่แตกต่างกันและgawkไม่สามารถใช้ได้ตามค่าเริ่มต้นในสถานที่ส่วนใหญ่
Oli

6
OP ขอวิธีแก้ปัญหา awk โดยเฉพาะดังนั้นฉันจึงไม่คิดว่านี่เป็นคำตอบ
Joppe

6
@Joppe คุณไม่สามารถแก้ปัญหา awk ถ้าไม่มีวิธีแก้ปัญหา ในบรรทัดที่ 3 ฉันอธิบายว่า AWK ไม่สนับสนุนกลุ่มที่จับภาพและฉันให้ทางเลือกซึ่ง OP เห็นได้อย่างชัดเจนเพราะคำตอบนี้ได้รับการยอมรับ ฉันจะตอบคำถามนี้ได้อย่างไร
Peter Tillemans

335

ด้วยเพ่งพิศคุณสามารถใช้matchฟังก์ชั่นในการจับภาพกลุ่มวงเล็บ

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

ตัวอย่าง:

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

cdเอาท์พุท

สังเกตการใช้งานเพ่งพิศซึ่งใช้งานคุณสมบัติดังกล่าว

สำหรับทางเลือกในแบบพกพาคุณสามารถบรรลุผลที่คล้ายกันด้วยและmatch()substr

ตัวอย่าง:

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

cdเอาท์พุท


4
ใช่รูปแบบ gxxx มีความดีและพลัง GNU เพิ่มเติมมากมาย
Peter Tillemans

ทำงานใน BusyBox awk เช่นกัน
MrMas

32

นี่คือสิ่งที่ฉันต้องการตลอดเวลาดังนั้นฉันจึงสร้างฟังก์ชั่นทุบตีสำหรับมัน มันขึ้นอยู่กับคำตอบของเกล็นแจ็คแมน

คำนิยาม

เพิ่มสิ่งนี้ลงใน. bash_profile เป็นต้น

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

การใช้

จับ regex สำหรับแต่ละบรรทัดในไฟล์

$ cat filename | regex '.*'

จับกลุ่มการจับภาพ regex ที่ 1 สำหรับแต่ละบรรทัดในไฟล์

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

2
มันแตกต่างจากการใช้งานgrep -oอย่างไร?
bfontaine

@bfontaine grep -oส่งออกกลุ่มที่ถูกจับได้หรือไม่
Olle Härstedt

1
@ OlleHärstedtไม่เป็นไปไม่ได้ ครอบคลุมเฉพาะกรณีการใช้งานของคุณเมื่อคุณไม่มีกลุ่มจับภาพ ในกรณีที่ว่าจะได้รับน่าเกลียดกับล่ามโซ่grep -o's
bfontaine

15

คุณสามารถใช้ 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/

12
+1 นอกจากนี้ยังมี awk ใด ๆ :awk 'match($0, /.*(http.*?)\$/) { print substr($0,RSTART,RLENGTH) }'
เอ็ดมอร์ตัน

5
นั่นคือสิ่งที่คำตอบของเกล็นแจ็คแมนพูดได้ค่อนข้างมาก
rampion

1
เอ็ดมอร์ตัน: นั่นสมควรได้รับคำตอบระดับสูงฉันจะบอกว่า แก้ไข: uhm ... ที่พิมพ์RewriteRule (.*) http://www.mysite.net/$สำหรับฉันซึ่งเป็นมากกว่ากลุ่มย่อย
rampion


4

คุณสามารถจำลองการจับภาพในวานิลลา awk ได้เช่นกันโดยไม่มีส่วนขยาย มันไม่ง่ายแม้ว่า:

ขั้นตอนที่ 1 ใช้ gensub เพื่อล้อมรอบการจับคู่กับอักขระบางตัวที่ไม่ปรากฏในสตริงของคุณ ขั้นตอนที่ 2 ใช้แบ่งกับตัวละคร ขั้นตอนที่ 3 องค์ประกอบอื่น ๆ ในอาร์เรย์ที่แยกออกคือกลุ่มการจับภาพของคุณ

$ echo 'ab cb ad' | awk '{แยก (gensub (/ a ./, SUBSEP "&" SUBSEP, "g", $ 0), หมวก, SUBSEP); ฝาพิมพ์ [2] "|" หมวก [4]; }'
AB | โฆษณา

3
ฉันเกือบจะแน่ใจว่าgensubเป็นgawkฟังก์ชั่นเฉพาะ สิ่งใดที่คุณได้รับจาก awk ของคุณถ้าคุณพิมพ์awk --version-?) โชคดีทุกคน.
shellter

6
ฉันมั่นใจอย่างเต็มที่ว่า gensub นั้นเป็น gawk-ism แต่ BusyBox awk ก็มีเช่นกัน คำตอบนี้สามารถใช้งานได้โดยใช้ gsub แต่:echo 'ab cb ad' | awk '{gsub(/a./,SUBSEP"&"SUBSEP);split($0,cap,SUBSEP);print cap[2]"|"cap[4]}'
dubiousjim

3
gensub () เป็นส่วนเสริมเพ่งพิศคู่มือของเพ่งพิศพูดอย่างนั้น ตัวแปร awk อื่น ๆ อาจใช้งานได้ แต่ก็ยังไม่ได้ POSIX ลอง gawk --posix '{gsub (... )}' แล้วมันจะบ่น
MestreLion

2
@MestreLion gawk --posix '{gensub(...)}'คุณหมายความว่ามันจะบ่นสำหรับ
dubiousjim

1
แม้ว่าคุณจะผิดเกี่ยวกับPOSIX awk ที่มีgensubฟังก์ชั่นตัวอย่างของคุณนำไปใช้กับสถานการณ์ที่ จำกัด มาก: รูปแบบทั้งหมดถูกจัดกลุ่ม แต่ก็ไม่สามารถจับคู่สิ่งที่เหมือนkey=(value)เมื่อฉันต้องการแยกเฉพาะvalueส่วน
Meow

2

ฉันต่อสู้เล็กน้อยด้วยฟังก์ชั่นทุบตีที่ห่อคำตอบของ Peter Tillemans แต่นี่คือสิ่งที่ฉันเกิดขึ้น:

ฟังก์ชัน regex {perl -n -e "/ $ 1 / && printf \"% s \ n \ "," '$ 1'}

ฉันพบสิ่งนี้ทำงานได้ดีกว่าฟังก์ชั่นทุบตี awk-based aws สำหรับอาร์กิวเมนต์การแสดงออกปกติต่อไปนี้เพราะฉันไม่ต้องการให้พิมพ์ "ms"

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

ฉันชอบโซลูชันนี้เนื่องจากคุณสามารถเห็นส่วนต่าง ๆ ของกลุ่มที่กำหนดขอบเขตการดักจับในขณะที่ไม่ใช้งาน อย่างไรก็ตามมีใครบางคนอธิบายวิธีการทำงานของมันได้หรือไม่ ฉันไม่สามารถรับไวยากรณ์ perl นี้เพื่อทำงานอย่างถูกต้องใน BASH เพราะฉันไม่เข้าใจเป็นอย่างดี - โดยเฉพาะเครื่องหมายอัญประกาศคู่ / เดี่ยว$1
Demis

ไม่ใช่สิ่งที่ฉันได้ทำมาก่อนหรือตั้งแต่นั้น แต่มองย้อนกลับไปว่ากำลังทำอะไรอยู่คือการเชื่อมสองสายเข้าด้วยกันสตริงแรกอยู่ในเครื่องหมายคำพูดคู่ (สตริงแรกนี้มีเครื่องหมายคำพูดคู่ฝังอยู่ . จากนั้นผลลัพธ์ของการต่อข้อมูลนั้นจะถูกส่งเป็นอาร์กิวเมนต์ของ perl -e นอกจากนี้คุณจำเป็นต้องรู้ว่า $ 1 แรก (อันที่อยู่ในเครื่องหมายคำพูดคู่) จะถูกแทนที่ด้วยอาร์กิวเมนต์แรกของฟังก์ชันในขณะที่อีก $ 1 (อันที่อยู่ในเครื่องหมายคำพูดเดี่ยว) จะไม่มีการเปลี่ยนแปลง ดูตัวอย่างนี้
wytten

ฉันเข้าใจแล้วว่ามันสมเหตุสมผลมากกว่านี้ ดังนั้นที่ไหนในคำสั่ง perl คำจำกัดความการจับคู่การจับคู่ / กลุ่ม regex? ฉันเห็นคุณเขียน'([0-9]*)ms$'- นั่นเป็นข้อโต้แย้ง (และอีกสตริงหนึ่งโต้แย้ง)? และเอาท์พุทจากperl -eการถูกใส่เข้าไปในprintfคำสั่งของ bash แล้ว, เพื่อแทนที่%s, ใช่ไหม? ขอบคุณฉันหวังว่าจะใช้มัน
มิส

1
คุณผ่านนิพจน์ทั่วไปที่อยู่ในเครื่องหมายคำพูดเดี่ยวเป็นอาร์กิวเมนต์ แต่เพียงผู้เดียวกับฟังก์ชันทุบตี regex ตัวอย่าง
wytten
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.