grep เพื่อค้นหาอินสแตนซ์ของ“ Foo” ที่“ บาร์” ไม่ปรากฏภายใน 10 บรรทัด


10

สมมติว่าฉันต้องการค้นหาต้นไม้ทั้งหมดสำหรับไฟล์ CPP ทั้งหมดที่เกิด "Foo" ฉันอาจทำ:

find . -name "*.cpp" | xargs grep "Foo"

ตอนนี้สมมติว่าฉันต้องการแสดงเฉพาะอินสแตนซ์ที่มีสตริงอื่น ๆ กล่าวว่า "บาร์" ไม่ได้เกิดขึ้นภายใน 3 บรรทัดของผลลัพธ์ก่อนหน้า

รับสองไฟล์:

a.cpp

1 Foo
2 qwerty
3 qwerty

b.cpp

1 Foo
2 Bar
3 qwerty

ฉันต้องการสร้างการค้นหาทั่วไปที่พบ "Foo" จาก a.cpp แต่ "Foo" จาก b.cpp ไม่

มีวิธีการทำสิ่งนี้ในวิธีที่ค่อนข้างง่ายหรือไม่?


บางทีโซลูชันอาจอยู่ในตัวเลือก grep -A และ / หรือ grep -B และ / หรือ grep -C ฉันพยายาม แต่ไม่มีความสำเร็จ ....
maurelio79

@ maurelio79: ทฤษฎีปัจจุบันของฉันคือสิ่งนี้ Grep สำหรับ "Foo" ใช้ -A 10 สำหรับบริบท ไปป์ที่ grep -v Bar ไปป์ที่ sed เพื่อรับชื่อไฟล์ & หมายเลขบรรทัด ไพพ์ไปที่ (อะไร?) เพื่อพิมพ์บรรทัดนั้น
John Dibling

คำตอบ:


17

ด้วยpcregrep:

pcregrep --include='\.cpp$' -rnM 'Foo(?!(?:.*\n){0,2}.*Bar)' .

กุญแจอยู่ใน-Mตัวเลือกที่ไม่ซ้ำกันpcregrepและใช้เพื่อจับคู่หลายบรรทัด ( pcregrepดึงข้อมูลเพิ่มเติมจากไฟล์อินพุตตามความจำเป็นเมื่อเดิน RE ต้องการ)

(?!...)เป็นตัวดำเนินการ RE เชิงลบสำหรับการดูล่วงหน้า Foo(?!...)จับคู่Fooตราบ...ใดที่ไม่ตรงกับสิ่งต่อไปนี้

...เป็น(?:.*\n){0,2}.*Bar( .ที่ไม่ตรงกับตัวอักษรขึ้นบรรทัดใหม่) ที่เป็น 0-2 Barเส้นตามด้วยเส้นที่มี


+1: ยอดเยี่ยม ขอบคุณมาก; ฉันแน่ใจว่ามันไม่ใช่เรื่องง่ายที่จะเข้าใจ regex ที่ถูกต้อง ฉันซาบซึ้งในความพยายามของคุณมาก ดูเหมือนว่าจะทำงานได้ตรงตามที่ฉันต้องการ
John Dibling

2
คำถามด้านถ้าคุณสนใจที่จะตอบ คุณรู้จักมาได้pcregrepอย่างไร? ฉันไม่เคยได้ยินมาก่อน
John Dibling

@JohnDibling ผมเองพบเมื่อเร็ว ๆ นี้ใน unix.SE RE นั้นไม่ซับซ้อนเป็นพิเศษโดยเฉพาะอย่างยิ่งเมื่อคุณคุ้นเคยกับผู้ดำเนินการ RE (?!...)เชิงลบที่ดูล่วงหน้า perl
Stéphane Chazelas

9

ไม่เป็นไรเพียงใช้pcregrepตามที่แนะนำโดย @StephaneChazelas


สิ่งนี้น่าจะใช้ได้:

$ find . -name "*.cpp" | 
    while IFS= read -r file; do 
      grep -A 3 Foo "$file" | grep -q Bar || echo "$file"; 
    done 

แนวคิดคือการใช้-Aสวิตช์ของ grep เพื่อส่งออกเส้นที่ตรงกันและ N บรรทัดต่อไปนี้ จากนั้นคุณจะส่งผลลัพธ์ผ่าน a grep Barและหากไม่ตรงกับ (ออก> 0) คุณจะแสดงชื่อของไฟล์

หากคุณรู้ว่าคุณมีชื่อไฟล์มีเหตุผล (ไม่มีช่องว่างบรรทัดใหม่หรืออักขระแปลก ๆ ) คุณสามารถทำให้:

$ for file in $(find . -name "*.cpp"); do 
   grep -A 3 Foo "$file" | grep -q Bar || echo "$file"; 
  done 

ตัวอย่างเช่น:

terdon@oregano foo $ cat a.cpp 
1 Foo
2 qwerty
3 qwerty
terdon@oregano foo $ cat b.cpp 
1 Foo
2 Bar
3 qwerty
terdon@oregano foo $ cat c.cpp 
1 Foo
2 qwerty
3 qwerty
4 qwerty
5. Bar
terdon@oregano foo $ for file in $(find . -name "*.cpp"); do grep -A 3 Foo "$file" | grep -q Bar || echo "$file"; done 
./c.cpp
./a.cpp

โปรดทราบว่าc.cppจะถูกส่งกลับแม้จะมีBarเพราะบรรทัดที่มีBarมากกว่า 3 Fooเส้นหลัง คุณสามารถควบคุมจำนวนบรรทัดที่คุณต้องการค้นหาโดยเปลี่ยนค่าที่ส่งไปยัง-A:

$ for file in $(find . -name "*.cpp"); do 
   grep -A 10 Foo "$file" | grep -q Bar || echo "$file"; 
  done 
./a.cpp

นี่คืออันที่สั้นกว่า (สมมติว่าคุณใช้bash):

$ shopt -s globstar 
$ for file in **/*cpp; do 
    grep -A 10 Foo "$file" | grep -q Bar || echo "$file"; 
  done

สิ่งสำคัญ

ตามที่ Stephane Chazelas ชี้ให้เห็นในความคิดเห็นโซลูชันดังกล่าวจะพิมพ์ไฟล์ที่ไม่มีFooอยู่ด้วย คนนี้หลีกเลี่ยงที่:

for file in **/*cpp; do 
  grep -qm 1 Foo "$file" && 
  (grep -A 3 Foo "$file" | grep -q Bar || echo "$file"); 
done

+1 เรียบร้อย ซับซ้อนน้อยกว่าที่ฉันหวังไว้ แต่ก็ไม่เลวเลย
John Dibling

นั่นถือว่า "Foo" เกิดขึ้นเพียงครั้งเดียว ซึ่งจะรายงานไฟล์ที่ไม่มีFooอยู่ด้วย คุณไม่มีเครื่องหมายคำพูด
Stéphane Chazelas

@StephaneChazelas ขอบคุณราคาคงที่ คุณค่อนข้างที่เหมาะสมเกี่ยวกับการรายงานไฟล์ที่มีไม่มีFooและฉันคงที่ Fooแต่ผมไม่เห็นจุดของคุณเกี่ยวกับกรณีหลาย ควรจัดการกับพวกเขาอย่างถูกต้อง
terdon

@JohnDibling ดูการอัปเดต
terdon

1
มันจะไม่รายงานไฟล์ที่มี "Foo" 100 บรรทัดตามด้วย "บาร์"
Stéphane Chazelas

0

ยังไม่ทดลองฉันอยู่ในโทรศัพท์:

find . -name "*.cpp" | xargs awk '/foo/{t=$0;c=10}/bar/{c=0;t=""}c{c--}t&&!c{print t;t=""}END&&t{print t}' 

อะไรแบบนั้น.

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