จะพิมพ์รูปแบบ regex ที่ตรงกันโดยใช้ awk ได้อย่างไร


112

โดยใช้awkฉันต้องหาคำในไฟล์ที่ตรงกับรูปแบบนิพจน์ทั่วไป

ฉันเพียงต้องการพิมพ์คำว่าจับคู่กับรูปแบบ

ดังนั้นหากอยู่ในบรรทัดฉันมี:

xxx yyy zzz

และรูปแบบ:

/yyy/

ฉันต้องการเพียงแค่:

yyy

แก้ไข: ขอบคุณkurumiฉันจัดการเขียนสิ่งนี้:

awk '{
        for(i=1; i<=NF; i++) {
                tmp=match($i, /[0-9]..?.?[^A-Za-z0-9]/)
                if(tmp) {
                        print $i
                }
        }
}' $1

และนี่คือสิ่งที่ฉันต้องการ :) ขอบคุณมาก!


1
@maxtaldykin คุณช่วยย้ายคำตอบของคุณจากคำถามไปเป็นคำตอบแยกกันได้ไหม?
kenorb

2
คุณไม่จำเป็นต้องทำtmp=match($i, /regexp);if(tmp){}ก็ควรทำได้if(tmp ~ $i){}เพราะ~หมายถึง "ตรงกับ regexp"
JustinCB

คำตอบ:


148

นี่คือขั้นพื้นฐาน

awk '/pattern/{ print $0 }' file

ขอawkให้ค้นหาpatternโดยใช้//จากนั้นพิมพ์บรรทัดซึ่งโดยค่าเริ่มต้นเรียกว่าเรกคอร์ดแสดงด้วย $ 0 อย่างน้อยอ่านค่าเอกสาร

หากคุณต้องการพิมพ์เฉพาะคำที่ตรงกัน

awk '{for(i=1;i<=NF;i++){ if($i=="yyy"){print $i} } }' file

50
เนื่องจากprintเป็นการกระทำเริ่มต้น: awk '/pattern/' fileจะเพียงพอ
Johnsyweb

18
@Johnsyweb ใช่ฉันรู้ข้อเท็จจริงนี้ สำหรับมือใหม่อย่าง marverix มันควรจะเป็นภาพที่ชัดเจนมากขึ้น
kurumi

21
ฉันไม่สงสัยในความรู้ของคุณ อย่างไรก็ตามข้อมูลอาจเป็นประโยชน์สำหรับผู้อื่นในการค้นหาคำตอบนี้
Johnsyweb

2
หมายเหตุ: @marverix จะต้องทำการบ้านอีกเล็กน้อยเพื่อให้for-loop ทำงานได้ถ้า (a) "yyy" เป็นนิพจน์ทั่วไปไม่ใช่สตริงตรงและ (b) หาก "yyy" ไม่ตรงกับฟิลด์ทั้งหมดภายใน บันทึก.
Johnsyweb

8
มันจะไม่$i=="yyy"; มันจะ$i ~ /yyy/เป็นนิพจน์ทั่วไป
JustinCB

120

ดูเหมือนว่าคุณกำลังพยายามเลียนแบบgrep -oพฤติกรรมของ GNU สิ่งนี้จะทำให้คุณต้องการเพียงการแข่งขันนัดแรกในแต่ละบรรทัด:

awk 'match($0, /regex/) {
    print substr($0, RSTART, RLENGTH)
}
' file

นี่คือตัวอย่างโดยใช้การใช้งาน GNU awk():

awk 'match($0, /a.t/) {
    print substr($0, RSTART, RLENGTH)
}
' /usr/share/dict/words | head
act
act
act
act
aft
ant
apt
art
art
art

อ่านข้อมูลเกี่ยวกับmatch, substr, RSTARTและRLENGTHในawkคู่มือ

หลังจากนั้นคุณอาจต้องการขยายเพื่อจัดการกับการแข่งขันหลายรายการในบรรทัดเดียวกัน


หมายเหตุ: เพื่อตอบส่วนสุดท้ายนั้นโครงสร้างทั้งหมดที่จำเป็นอยู่ใน คำตอบของคุรุมิและของฉันเอง
Johnsyweb

คำตอบที่ดี ฉันต้องการคำอธิบายที่นี่เพราะฉันขี้เกียจ แต่นั่นเป็นเหตุผลที่ฉันใช้ AWK!
lukas.pukenis

จะเกิดอะไรขึ้นถ้าฉันต้องการทำอะไรบางอย่างกับผลการแข่งขันยกเว้นการพิมพ์? ตัวอย่างเช่นฉันต้องการเพิ่มรายการที่ตรงกันทั้งหมดลงในอาร์เรย์
Evya2005

@ evya2005: คุณสามารถแทนที่การพิมพ์โทรรอนด้วยงานที่คุณต้องการได้
Johnsyweb

มันไม่ได้ผลสำหรับฉัน เฉพาะงานพิมพ์ ขอดูตัวอย่างได้ไหม
Evya2005

36

gawkสามารถรับส่วนที่ตรงกันของทุกบรรทัดโดยใช้สิ่งนี้เป็นการกระทำ:

{ if (match($0,/your regexp/,m)) print m[0] }

match (string, regexp [, array]) หากมีอาร์เรย์อยู่จะถูกล้างจากนั้นองค์ประกอบ zeroth ของอาร์เรย์จะถูกตั้งค่าเป็นส่วนทั้งหมดของสตริงที่จับคู่โดย regexp หาก regexp มีวงเล็บองค์ประกอบที่จัดทำดัชนีจำนวนเต็มของอาร์เรย์จะถูกตั้งค่าให้มีส่วนของสตริงที่ตรงกับนิพจน์ย่อยในวงเล็บที่สอดคล้องกัน http://www.gnu.org/software/gawk/manual/gawk.html#String-Functions


13

หากคุณสนใจเฉพาะบรรทัดสุดท้ายของการป้อนข้อมูลและคุณคาดว่าจะพบรายการที่ตรงกันเพียงรายการเดียว (เช่นส่วนหนึ่งของบรรทัดสรุปของคำสั่งเชลล์) คุณสามารถลองใช้โค้ดขนาดกะทัดรัดนี้ได้ซึ่งนำมาจากHow to print regexp ใช้ "awk"? :

$ echo "xxx yyy zzz" | awk '{match($0,"yyy",a)}END{print a[0]}'
yyy

หรือเวอร์ชันที่ซับซ้อนกว่าพร้อมผลลัพธ์บางส่วน:

$ echo "xxx=a yyy=b zzz=c" | awk '{match($0,"yyy=([^ ]+)",a)}END{print a[1]}'
b

คำเตือน: awk match()ฟังก์ชันที่มีสามอาร์กิวเมนต์มีอยู่ในgawkเท่านั้นไม่ใช่ในmawk

นี่คือวิธีการแก้ปัญหาที่ดีอื่นใช้regex lookbehindในแทนgrep awkโซลูชันนี้มีข้อกำหนดต่ำกว่าสำหรับการติดตั้งของคุณ:

$ echo "xxx=a yyy=b zzz=c" | grep -Po '(?<=yyy=)[^ ]+'
b

ทำไมคุณถึงเพิ่ม "tail -n1" สิ่งนี้ควรใช้งานได้ดีหากไม่มีใช่หรือไม่?
Arthur Accioly

1
@ArthurAccioly ถูกต้อง. ฉันใช้คำนี้เพื่อดึงเวลาไปกลับโดยเฉลี่ยจากการโทรแบบ ping นั่นคือที่มา ตลกดีที่ต้องใช้เวลา 4 ปีในการค้นพบ;)
Daniel Alder

12

หาก Perl เป็นตัวเลือกคุณสามารถลองสิ่งนี้:

perl -lne 'print $1 if /(regex)/' file

ในการใช้การจับคู่แบบไม่คำนึงถึงขนาดตัวพิมพ์ให้เพิ่มiตัวปรับแต่ง

perl -lne 'print $1 if /(regex)/i' file

ในการพิมพ์ทุกอย่างหลังการแข่งขัน:

perl -lne 'if ($found){print} else{if (/regex(.*)/){print $1; $found++}}' textfile

ในการพิมพ์การแข่งขันและทุกอย่างหลังการแข่งขัน:

perl -lne 'if ($found){print} else{if (/(regex.*)/){print $1; $found++}}' textfile

5

การใช้ sed สามารถทำให้เกิดความสง่างามได้ในสถานการณ์เช่นนี้ ตัวอย่าง (แทนที่บรรทัดด้วยกลุ่มที่ตรงกัน "yyy" จากบรรทัด):

$ cat testfile
xxx yyy zzz
yyy xxx zzz
$ cat testfile | sed -r 's#^.*(yyy).*$#\1#g'
yyy
yyy

หน้าคู่มือที่เกี่ยวข้อง: https://www.gnu.org/software/sed/manual/sed.html#Back_002dreferences-and-Subexpressions


1
สำหรับ non-gnu sed การแก้ปัญหาเป็นดังนี้:sed -n 's/^.*\(yyy\).*$/\1/gp' < testfile
Grigory Entin

1
@GrigoryEntin - bsd sed ทำงานได้ดีกับคำตอบเดิม สวิตช์ regex แบบขยายที่รองรับโดย POSIX คือ -E แต่ใน FreeBSD อย่างน้อย -r จะเหมือนกับ -E (-r เพิ่มในปี 2010) อย่างไรก็ตามลองใช้ -E (gnu sed เพิ่ม -E ใน 4.3)
Juan

4

ปิดหัวข้อนี้ทำได้โดยใช้ grep ด้วยเพียงแค่โพสต์ไว้ที่นี่เผื่อว่าใครกำลังมองหาวิธีแก้ grep

echo 'xxx yyy zzze ' | grep -oE 'yyy'

วิธีง่ายๆในการคว้าแม้จะใช้ regex สิ่งที่ฉันต้องการ ขอบคุณ!
Marquee

สิ่งนี้ใช้ได้กับฉัน กรณีของฉันเหมือน: echo "web_port = 8080, shutdown_port = 8005" | grep -oE "web_port = [0-9] +" # return 8080
Robb Tsang

0

หากคุณทราบว่าคอลัมน์ใดที่คุณต้องการหาข้อความ / รูปแบบ (เช่น "yyy") คุณสามารถตรวจสอบคอลัมน์นั้น ๆ เพื่อดูว่าตรงกันหรือไม่และพิมพ์ออกมา

ตัวอย่างเช่นไฟล์ที่มีเนื้อหาต่อไปนี้ (เรียกว่าasdf.txt )

xxx yyy zzz

ในการพิมพ์คอลัมน์ที่สองหากตรงกับรูปแบบ "yyy" คุณสามารถทำสิ่งนี้:

awk '$2 ~ /yyy/ {print $2}' asdf.txt

โปรดทราบว่าสิ่งนี้จะจับคู่บรรทัดโดยทั่วไปโดยที่คอลัมน์ที่สองมี "yyy" อยู่ด้วยเช่นนี้

xxx yyyz zzz
xxx zyyyz
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.