ฉันจะ grep สำหรับหลายรูปแบบในหลายบรรทัดได้อย่างไร


19

เพื่อความแม่นยำ

Some text
begin
Some text goes here.
end
Some more text

และฉันต้องการแยกบล็อกทั้งหมดที่เริ่มต้นจาก "เริ่มต้น" จนถึง "สิ้นสุด"

ด้วย awk เราสามารถทำเช่นawk '/begin/,/end/' textนั้น

จะทำอย่างไรกับ grep?


คำตอบ:


14

อัปเดตวันที่ 18 พ.ย. 2559 (เนื่องจากการเปลี่ยนแปลงพฤติกรรมของ grep: grep ด้วยพารามิเตอร์ -P ตอนนี้ไม่สนับสนุน^และ$จุดยึด [ใน Ubuntu 16.04 กับเคอร์เนล v: 4.4.0-21-generic]) ( แก้ไขไม่ถูกต้อง (ไม่ใช่ - )

$ grep -Pzo "begin(.|\n)*\nend" file
begin
Some text goes here.  
end

หมายเหตุ: สำหรับคำสั่งอื่น ๆ ให้แทนที่ตัวยึด '^' & '$' ด้วยตัวยึดบรรทัดใหม่'\n' ______________________________

ด้วยคำสั่ง grep:

grep -Pzo "^begin\$(.|\n)*^end$" file

หากคุณไม่ต้องการรูปแบบ "เริ่มต้น" และ "สิ้นสุด" ให้ใช้ grep ด้วย Lookbehind และ Lookahead support

grep -Pzo "(?<=^begin$\n)(.|\n)*(?=\n^end$)" file

นอกจากนี้คุณสามารถใช้การ\Kแจ้งเตือนแทน Lookbehind ยืนยัน

grep -Pzo "^begin$\n\K(.|\n)*(?=\n^end$)" file

\Kตัวเลือกละเว้นทุกอย่างก่อนการจับคู่รูปแบบและละเว้นรูปแบบเอง
\nใช้สำหรับหลีกเลี่ยงการพิมพ์บรรทัดว่างจากผลลัพธ์

หรือตามที่ @AvinashRaj แนะนำว่ามี grep ง่าย ๆ ง่ายๆดังต่อไปนี้:

grep -Pzo "(?s)^begin$.*?^end$" file

grep -Pzo "^begin\$[\s\S]*?^end$" file

(?s)บอก grep เพื่อให้จุดจับคู่อักขระขึ้นบรรทัดใหม่
[\s\S]จับคู่อักขระใด ๆ ที่เป็นช่องว่างหรือช่องว่างที่ไม่ใช่

และผลลัพธ์ของมันโดยไม่รวม "เริ่มต้น" และ "สิ้นสุด" มีดังต่อไปนี้:

grep -Pzo "^begin$\n\K[\s\S]*?(?=\n^end$)" file # or grep -Pzo "(?<=^begin$\n)[\s\S]*?(?=\n^end$)"

grep -Pzo "(?s)(?<=^begin$\n).*?(?=\n^end$)" file

ดูการทดสอบทั้งหมดของคำสั่งทั้งหมดที่นี่ ( ล้าสมัยเนื่องจากการทำงาน grep กับพารามิเตอร์ -P มีการเปลี่ยนแปลง )

บันทึก:

^ชี้จุดเริ่มต้นของบรรทัดและ$ชี้จุดสิ้นสุดของบรรทัด สิ่งเหล่านี้ถูกเพิ่มลงใน "เริ่มต้น" และ "สิ้นสุด" เพื่อจับคู่พวกเขาหากพวกเขาอยู่คนเดียวในบรรทัด
ในสองคำสั่งฉันหนี$เพราะมันยังใช้สำหรับ "การทดแทนคำสั่ง" ( $(command)) ที่ช่วยให้การส่งออกของคำสั่งเพื่อแทนที่ชื่อคำสั่ง

จาก grep มนุษย์:

-o, --only-matching
      Print only the matched (non-empty) parts of a matching line,
      with each such part on a separate output line.

-P, --perl-regexp
      Interpret PATTERN as a Perl compatible regular expression (PCRE)

-z, --null-data
      Treat the input as a set of lines, each terminated by a zero byte (the ASCII 
      NUL character) instead of a newline. Like the -Z or --null option, this option 
      can be used with commands like sort -z to process arbitrary file names.

เปลี่ยน grep ของคุณgrep -Pzo "(?<=begin\n)(.|\n)*(?=\nend)" fileไม่ต้องการพิมพ์\nตัวอักษรที่มีอยู่ในบรรทัดเริ่มต้น
Avinash Raj

ใช้ตัวดัดแปลง DOTALL เพื่อสร้างจุดเพื่อจับคู่ตัวอักษรขึ้นบรรทัดใหม่ด้วยgrep -Pzo "(?s)begin.*?end" file
Avinash Raj

หรือเพียงแค่grep -Pzo "begin[\s\S]*?end" file
Avinash Raj

1
ข้อเสียเปรียบไม่ทำงาน มันสร้างข้อผิดพลาด: grep: ein nicht geschütztes ^ oder $ wird mit -Pz nicht unterstütztการแปลข้อผิดพลาดเป็นสิ่งที่ต้องการ:grep: a not protected ^ or $ is not supported with -Pz
musbach

1
ใช่ฉันรู้ว่านั่นคือคำตอบของคุณ ฉันแน่ใจว่ามันใช้งานได้เมื่อคุณโพสต์สิ่งนี้ แต่ลองอีกครั้งในวันนี้ พฤติกรรมของgrepน่าจะเปลี่ยนไป
terdon

2

ในกรณีที่grepไวยากรณ์ของคุณไม่รองรับ perl ( -P) คุณสามารถลองเชื่อมโยงบรรทัดจับคู่รูปแบบจากนั้นขยายบรรทัดอีกครั้งดังนี้:

$ tr '\n' , < foo.txt | grep -o "begin.*end" | tr , '\n'
begin
Some text goes here.
end
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.