Неважливо, просто використовуйте, 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
ВАЖЛИВО
Як в коментарях зазначила Стефан Шазелас, вищезазначені рішення також друкують файли, які зовсім не містять Foo. Цей дозволяє уникнути:
for file in **/*cpp; do
grep -qm 1 Foo "$file" &&
(grep -A 3 Foo "$file" | grep -q Bar || echo "$file");
done