No importa, solo utilícelo pcregrepcomo lo sugiere @StephaneChazelas.
Esto debería funcionar:
$ find . -name "*.cpp" |
while IFS= read -r file; do
grep -A 3 Foo "$file" | grep -q Bar || echo "$file";
done
La idea es utilizar el -Ainterruptor de grep para generar las líneas coincidentes y las N líneas siguientes. Luego pasa el resultado a través de ay grep Barsi eso no coincide (salir> 0), entonces repite el nombre del archivo.
Si sabe que tiene nombres de archivo sanos (sin espacios, líneas nuevas u otros caracteres extraños), puede simplificarlo para:
$ for file in $(find . -name "*.cpp"); do
grep -A 3 Foo "$file" | grep -q Bar || echo "$file";
done
Por ejemplo:
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
Tenga en cuenta que c.cppse devuelve a pesar de contener Barporque la línea con Bares más de 3 líneas después Foo. Puede controlar el número de líneas que desea buscar cambiando el valor pasado a -A:
$ for file in $(find . -name "*.cpp"); do
grep -A 10 Foo "$file" | grep -q Bar || echo "$file";
done
./a.cpp
Aquí hay uno más corto (suponiendo que lo use bash):
$ shopt -s globstar
$ for file in **/*cpp; do
grep -A 10 Foo "$file" | grep -q Bar || echo "$file";
done
IMPORTANTE
Como Stephane Chazelas señaló en los comentarios, las soluciones anteriores también imprimirán archivos que no contienen Foonada. Este evita que:
for file in **/*cpp; do
grep -qm 1 Foo "$file" &&
(grep -A 3 Foo "$file" | grep -q Bar || echo "$file");
done