วิธีการจับคู่ที่ไม่ใช่โลภใน grep?


177

ฉันต้องการ grep คู่ที่สั้นที่สุดและรูปแบบควรเป็นดังนี้:

<car ... model=BMW ...>
...
...
...
</car>

... หมายถึงอักขระใด ๆ และอินพุตมีหลายบรรทัด


คำตอบ:


276

คุณกำลังมองหาการจับคู่ที่ไม่โลภ (หรือขี้เกียจ) ในการรับการจับคู่ที่ไม่โลภในนิพจน์ทั่วไปคุณต้องใช้ตัวแก้ไข?หลังตัวระบุ ตัวอย่างเช่นคุณสามารถเปลี่ยนไป.*.*?

โดยค่าเริ่มต้นgrepไม่รองรับการดัดแปลงที่ไม่ใช่โลภ แต่คุณสามารถใช้grep -Pเพื่อใช้ไวยากรณ์ของ Perl


3
eegg: dot all modifier เป็นที่รู้จักกันในชื่อ multiline มันเป็นตัวดัดแปลงที่เปลี่ยน "." พฤติกรรมการจับคู่เพื่อรวมบรรทัดใหม่ (โดยปกติจะไม่) ไม่มีปรับปรุงดังกล่าวใน grep แต่มีอยู่ในpcregrep
A. วิลสัน

1
การแก้ไข: ในรสชาติส่วนใหญ่ของ regex ที่สนับสนุนโหมดที่อนุญาตให้.จับคู่บรรทัดใหม่นั้นเรียกว่าDOTALLหรือโหมดบรรทัดเดียว Ruby เป็นเพียงหนึ่งเดียวที่เรียกมันว่าหลายสาย ในรสชาติอื่น ๆmultilineเป็นโหมดที่อนุญาตให้จุดยึด ( ^และ$) จับคู่ที่ขอบเขตของเส้น Ruby ไม่มีโหมดที่เทียบเท่ากันเพราะใน Ruby พวกเขาทำงานเช่นนั้นเสมอ
Alan Moore

5
-Pเป็นสิ่งใหม่ที่สมบูรณ์แบบสำหรับฉันฉันมีความสุขไปหลายปีและใช้เพียง-E... หลายปีที่สูญเปล่า! - หมายเหตุถึงตัวเอง: อ่านหน้าคนเป็นสิ่งปกติ (มากขึ้น!) คุณจะไม่แยกย่อยสวิตช์และตัวเลือกมากพอ
ocodo

29
ในบางแพลตฟอร์ม (เช่น Mac OS X) grepไม่รองรับ-Pแต่ถ้าคุณใช้egrepคุณสามารถใช้.*?รูปแบบเพื่อให้ได้ผลลัพธ์เดียวกัน egrep -o 'start.*?end' text.html
SaltyNuts

4
ในฐานะที่เป็นส่วนขยายของความคิดเห็น @SaltyNuts, Mac OS X ไม่สนับสนุน-Pแต่-Eจะเรียกegrepดังนั้นการ.*?ทำงานที่แนะนำก็ใช้ได้
Fredrik Erlandsson

83

จริงงานเฉพาะใน.*? perlฉันไม่แน่ใจว่าไวยากรณ์ grep Extended regexp ที่เทียบเท่าจะเป็นอย่างไร โชคดีที่คุณสามารถใช้ไวยากรณ์ Perl กับ grep ดังนั้นgrep -Pจะทำงาน แต่grep -Eซึ่งegrepจะไม่ทำงาน (มันจะโลภ)

ดูเพิ่มเติมที่: http://blog.vinceliu.com/2008/02/non-greedy-regular-expression-matching.html


9
grep -Pไม่ทำงานใน GNU grep 2.9 - เพียงแค่พยายามมัน (มันไม่ได้ผิดพลาดเพียงเงียบ ๆ ไม่ได้ใช้?Intertestly ไม่ไม่ได้. ไม่ได้ระดับเช่น:env|grep '[^\=]*\='
Roberto Tomás

2
ไม่มีgrep -Pตัวเลือกหรือpgrepคำสั่งในดาร์วิน / OS X 10.8 Mountain Lion แต่ใช้egrepงานได้ดี
Steve HHH

2
มีpgrepคำสั่งในกล่อง OS X 10.9 ของฉัน แต่เป็นโปรแกรมที่แตกต่างอย่างสิ้นเชิงโดยมีวัตถุประสงค์คือ "ค้นหาหรือกระบวนการสัญญาณตามชื่อ"
Desti

@ robertotomásตอบสนองต่อความคิดเห็นอายุ 6 ปีที่นี่ แต่ .... ฉันคิดอย่างนี้แล้วก็รู้ว่าฉันได้รับการแข่งขันที่ไม่ใช่โลภหลายครั้ง ตัวอย่างเช่นบนเทอร์มินัลสีคุณจะเห็นว่า `echo" bbbbb "| grep -P 'b. *? b'` ส่งคืนการแข่งขัน 2 ครั้ง
zzxyz

12

grep ของฉันที่ทำงานหลังจากลองสิ่งต่าง ๆ ในหัวข้อนี้:

echo "hi how are you " | grep -shoP ".*? "

ตรวจสอบให้แน่ใจว่าคุณต่อท้ายช่องว่างในแต่ละบรรทัดของคุณ

(Mine เป็นบรรทัดโดยการค้นหาบรรทัดเพื่อคายคำ)


3
-shoPดีช่วยในการจำ :)
Mariusz

echo "bbbbb" | grep -shoP 'b.*?b'เป็นประสบการณ์การเรียนรู้เล็กน้อย สิ่งเดียวที่ทำงานสำหรับฉันในแง่ของความขี้เกียจอย่างชัดเจนเช่นกัน
zzxyz

12

grep

สำหรับการแข่งขันที่ไม่โลภในgrepคุณสามารถใช้คลาสตัวละครที่ถูกทำให้ไร้ผล กล่าวอีกนัยหนึ่งพยายามหลีกเลี่ยงสัญลักษณ์แทน

ตัวอย่างเช่นหากต้องการดึงลิงก์ทั้งหมดไปยังไฟล์ jpeg จากเนื้อหาของหน้าคุณต้องใช้:

grep -o '"[^" ]\+.jpg"'

หากต้องการจัดการกับหลายบรรทัดให้ไพพ์อินพุตผ่านxargsก่อน ripgrepเพื่อประสิทธิภาพในการใช้งาน


3

คำตอบสั้น ๆ คือการใช้นิพจน์ทั่วไปถัดไป:

(?s)<car .*? model=BMW .*?>.*?</car>
  • (? s) - นี่ทำให้การจับคู่ข้ามหลายบรรทัด
  • . *? - จับคู่ตัวละครใด ๆ จำนวนครั้งในลักษณะขี้เกียจ (การแข่งขันน้อยที่สุด)

คำตอบที่ซับซ้อนมากขึ้นคือ:

(?s)<([a-z\-_0-9]+?) .*? model=BMW .*?>.*?</\1>

สิ่งนี้จะทำให้สามารถจับคู่ car1 และ car2 ในข้อความต่อไปนี้

<car1 ... model=BMW ...>
...
...
...
</car1>
<car2 ... model=BMW ...>
...
...
...
</car2>
  • (.. ) แสดงถึงกลุ่มการจับภาพ
  • \ 1 ในบริบทนี้ตรงกับ sametext ที่จับคู่ล่าสุดโดยจับหมายเลขกลุ่ม 1

1

ขออภัยฉันมาสาย 9 ปี แต่สิ่งนี้อาจใช้ได้กับผู้ชมในปี 2020

"Hello my name is Jello"ดังนั้นสมมติว่าคุณมีเส้นเหมือน ตอนนี้คุณต้องการค้นหาคำที่ขึ้นต้น'H'และลงท้ายด้วย'o'จำนวนอักขระใด ๆ และเราไม่ต้องการบรรทัดที่เราต้องการคำ ดังนั้นเราจึงสามารถใช้การแสดงออก:

grep "H[^ ]*o" file

นี่จะคืนคำทั้งหมด วิธีการทำงานของมันคือ: มันจะอนุญาตให้ตัวละครทุกตัวแทนที่จะเป็นตัวอักษรเว้นวรรคในระหว่างนี้วิธีที่เราสามารถหลีกเลี่ยงคำหลายคำในบรรทัดเดียวกัน

ตอนนี้คุณสามารถแทนที่อักขระช่องว่างด้วยอักขระอื่น ๆ ที่คุณต้องการ สมมติว่าบรรทัดแรกคือ"Hello-my-name-is-Jello"จากนั้นคุณสามารถหาคำโดยใช้นิพจน์:

grep "H[^-]*o" file

0

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

> grep -v -e 'clean\-\?up'
> grep --version grep (GNU grep) 2.20
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.