Peu importe, utilisez simplement pcregrepcomme suggéré par @StephaneChazelas.
Cela devrait fonctionner:
$ find . -name "*.cpp" |
while IFS= read -r file; do
grep -A 3 Foo "$file" | grep -q Bar || echo "$file";
done
L'idée est d'utiliser le -Acommutateur de grep pour sortir les lignes correspondantes et les N lignes suivantes. Vous passez ensuite le résultat à travers un grep Baret si cela ne correspond pas (exit> 0), alors vous faites écho au nom du fichier.
Si vous savez que vous avez des noms de fichiers raisonnables (pas d'espaces, de nouvelles lignes ou d'autres caractères étranges), vous pouvez simplifier pour:
$ for file in $(find . -name "*.cpp"); do
grep -A 3 Foo "$file" | grep -q Bar || echo "$file";
done
Par exemple:
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
Notez que c.cppest retourné malgré contenant Barcar la ligne avec Barest plus de 3 lignes après Foo. Vous pouvez contrôler le nombre de lignes que vous souhaitez rechercher en modifiant la valeur passée à -A:
$ for file in $(find . -name "*.cpp"); do
grep -A 10 Foo "$file" | grep -q Bar || echo "$file";
done
./a.cpp
Voici un plus court (en supposant que vous utilisez bash):
$ shopt -s globstar
$ for file in **/*cpp; do
grep -A 10 Foo "$file" | grep -q Bar || echo "$file";
done
IMPORTANT
Comme Stephane Chazelas l'a souligné dans les commentaires, les solutions ci-dessus imprimeront également des fichiers qui ne contiennent pas Foodu tout. Celui-ci évite que:
for file in **/*cpp; do
grep -qm 1 Foo "$file" &&
(grep -A 3 Foo "$file" | grep -q Bar || echo "$file");
done