Peu importe, utilisez simplement pcregrep
comme 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 -A
commutateur de grep pour sortir les lignes correspondantes et les N lignes suivantes. Vous passez ensuite le résultat à travers un grep Bar
et 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.cpp
est retourné malgré contenant Bar
car la ligne avec Bar
est 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 Foo
du 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