ไม่เป็นไรเพียงใช้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