จะ grep สำหรับข้อความในไฟล์และแสดงย่อหน้าที่มีข้อความได้อย่างไร


24

ด้านล่างนี้เป็นข้อความในไฟล์:

Pseudo name=Apple
Code=42B
state=fault

Pseudo name=Prance
Code=43B
state=good

ฉันต้องการ grep สำหรับ "42B" และรับผลลัพธ์จากข้อความข้างต้นเช่น:

Pseudo name=Apple
Code=42B
state=fault

ใครบ้างมีความคิดเกี่ยวกับวิธีการบรรลุการใช้grep/ awk/ sed?


คุณแท็กคำถามนี้ด้วย "grep" คุณกำลังมองหาโซลูชัน "grep" เท่านั้นใช่หรือไม่ ในคำถามที่คุณระบุ awk & sed ด้วย เราสามารถเพิ่มแท็กเหล่านั้นได้ไหม ฉันไม่แน่ใจในความตั้งใจของคุณเมื่อฉันแก้ไขคำถามเมื่อคืน
slm

คำตอบ:


38

กับ awk

awk -v RS='' '/42B/' file

RS=เปลี่ยนตัวคั่นเร็กคอร์ดอินพุตจากบรรทัดใหม่เป็นบรรทัดว่าง ถ้าเขตข้อมูลใด ๆ ในระเบียนประกอบด้วย/42B/ระเบียนนั้น

''(สตริง null) เป็นค่าเวทย์มนตร์ที่ใช้เพื่อแสดงบรรทัดว่างตาม POSIX :

หากRSเป็นโมฆะเร็กคอร์ดจะถูกคั่นด้วยลำดับที่ประกอบด้วย<newline>บรรทัดว่างบวกหนึ่งหรือมากกว่าบรรทัดว่างที่นำหน้าหรือต่อท้ายจะไม่ส่งผลให้เร็กคอร์ดว่างที่จุดเริ่มต้นหรือจุดสิ้นสุดของอินพุตและ a <newline>จะเป็นตัวคั่นฟิลด์เสมอ ไม่ว่ามูลค่าของFSจะเป็นเท่าไหร่ก็ตาม

ย่อหน้าเอาต์พุตจะไม่ถูกแยกเนื่องจากตัวคั่นเอาต์พุตยังคงเป็นบรรทัดใหม่ เพื่อให้แน่ใจว่ามีบรรทัดว่างระหว่างย่อหน้าเอาต์พุตให้ตั้งค่าตัวคั่นเร็กคอร์ดเอาต์พุตเป็นสองบรรทัดใหม่:

awk -v RS='' -v ORS='\n\n' '/42B/' file

1
+1 สำหรับโซลูชันที่สง่างาม คุณไม่จำเป็นต้องเปลี่ยนเส้นทางไฟล์แม้ว่า ...
jasonwryan

นิ้วอยู่บนอัตโนมัติ
llua

2
@jasonwryan เว้นแต่คุณจะต้องเข้าถึงชื่อไฟล์ภายใน awk ( FILENAME) ก็ไม่เป็นความคิดที่ดีที่จะใช้การเปลี่ยนเส้นทางเป็นปัญหาที่หลีกเลี่ยงสำหรับชื่อไฟล์ที่มี=หรือเริ่มต้นด้วย-(หรือถูก-) ทำให้ข้อความผิดพลาดที่สอดคล้องกันและหลีกเลี่ยงการทำงานawkหรือดำเนินการ การเปลี่ยนเส้นทางอื่น ๆ หากไฟล์อินพุตไม่สามารถเปิดได้
Stéphane Chazelas

14

สมมติว่าข้อมูลมีโครงสร้างเพื่อให้เป็นบรรทัดก่อนและหลังที่คุณต้องการคุณสามารถใช้สวิตช์-A(หลังจาก) และ-B(ก่อน) ของ grep เพื่อบอกให้รวม 1 บรรทัดก่อนการแข่งขันและ 1 บรรทัดหลังจากนั้น:

$ grep -A 1 -B 1 "42B" sample.txt
Pseudo name=Apple
Code=42B
state=fault

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

$ grep -C 1 "42B" sample.txt
Pseudo name=Apple
Code=42B
state=fault

หากคุณต้องการเข้มงวดมากขึ้นเมื่อจับคู่หลายบรรทัดคุณสามารถใช้เครื่องมือpcregrepเพื่อจับคู่รูปแบบผ่านหลายบรรทัด:

$ pcregrep -M 'Pseudo.*\n.*42B.*\nstate.*' sample.txt
Pseudo name=Apple
Code=42B
state=fault

รูปแบบข้างต้นตรงกับดังนี้:

  • -M - หลายบรรทัด
  • 'Pseudo.*\n.*42B.*\nstate.*'- จับคู่กลุ่มของสตริงที่สตริงแรกเริ่มต้นด้วยคำ"Pseudo"ตามด้วยตัวอักษรใด ๆ จนถึงจุดสิ้นสุดของบรรทัด\nตามด้วยตัวอักษรใด ๆ ขึ้นไปจนถึงสตริง"42B"ตามด้วยตัวอักษรใด ๆ จนถึงจนถึงปลายบรรทัดอื่น ( \n) ตามด้วยสตริง"state"ตามด้วยตัวละครใด ๆ

5
-C(บริบท) สามารถใช้เป็นทางลัดได้หาก-Aและ-Bเหมือนกัน
David Baggerman

@DavidBaggerman - ขอบคุณ เพิ่มเข้าไปในคำตอบ
slm

ทำไมคนหนึ่งลงคะแนน? คำถามนี้ตอบคำถาม
slm

4

อาจเป็นวิธีที่ง่ายในการทำกับ awk แต่ใน perl:

cat file | perl -ne 'BEGIN { $/="\n\n" }; print if $_ =~ /42B/;'

โดยทั่วไปแล้วจะบอกให้แบ่งไฟล์ออกเป็นส่วน ๆ ที่คั่นด้วยบรรทัดว่างแล้วพิมพ์เฉพาะชิ้นที่ตรงกับนิพจน์ปกติของคุณเท่านั้น


10
นี้ได้ง่ายโดยใช้ตัวเลือกและ shorthands และการสูญเสียการใช้งานที่ไร้ประโยชน์ของcat ; perl -00 -ne 'print if /42B/' file
tripleee

4

grepของรสชาติของ Unix บางมี-pธงสำหรับ "วรรค" ฉันรู้ว่าAIX ไม่

grep -p 42B <myfile>

จะทำสิ่งที่คุณขอตรงนั้น grep YMMV และ GNU ไม่มีค่าสถานะนี้


การมีแฟล็ก -p จะยอดเยี่ยม โดยเฉพาะอย่างยิ่งหากใช้ร่วมกับ -v เพื่อให้คุณสามารถแยกย่อหน้าทั้งหมดออกจากเอาต์พุต
IllvilJa

2

โซลูชัน perl อื่น ๆ ที่ไม่มีบรรทัดว่างต่อท้าย:

perl -00ne 'if ($_ =~ /42B/) {chomp($_); printf "%s\n",$_}' foo

ตัวอย่าง

% perl -00ne 'if ($_ =~ /42B/) {chomp($_); printf "%s\n",$_}' foo
Pseudo name=Apple
Code=42B
state=fault

% cat foo
Pseudo name=Apple
Code=42B
state=fault

Pseudo name=Prance
Code=43B
state=good

1
หรือสั้น (และอ่านได้มากขึ้น) เช่น triplee perl -00 -ne 'print if /42B/' fileเขียนในความคิดเห็น:
mivk
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.