Macht nichts, verwenden Sie einfach pcregrepwie vorgeschlagen von @StephaneChazelas.
Das sollte funktionieren:
$ find . -name "*.cpp" |
while IFS= read -r file; do
grep -A 3 Foo "$file" | grep -q Bar || echo "$file";
done
Die Idee ist, den -ASchalter von grep zu verwenden , um die übereinstimmenden Zeilen und die N folgenden Zeilen auszugeben. Sie übergeben das Ergebnis dann durch a grep Barund wenn dies nicht übereinstimmt (exit> 0), geben Sie den Namen der Datei wieder.
Wenn Sie wissen, dass Sie vernünftige Dateinamen haben (keine Leerzeichen, neuen Zeilen oder andere seltsame Zeichen), können Sie Folgendes vereinfachen:
$ for file in $(find . -name "*.cpp"); do
grep -A 3 Foo "$file" | grep -q Bar || echo "$file";
done
Beispielsweise:
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
Beachten Sie, dass dies c.cpptrotz Enthalten zurückgegeben wird, Barda die Zeile mit Barmehr als 3 Zeilen danach ist Foo. Sie können die Anzahl der zu durchsuchenden Zeilen steuern, indem Sie den Wert ändern, der an Folgendes übergeben wird -A:
$ for file in $(find . -name "*.cpp"); do
grep -A 10 Foo "$file" | grep -q Bar || echo "$file";
done
./a.cpp
Hier ist eine kürzere (vorausgesetzt, Sie verwenden bash):
$ shopt -s globstar
$ for file in **/*cpp; do
grep -A 10 Foo "$file" | grep -q Bar || echo "$file";
done
WICHTIG
Wie Stephane Chazelas in den Kommentaren hervorhob, drucken die oben genannten Lösungen auch Dateien, die überhaupt nicht enthalten sind Foo. Dieser vermeidet das:
for file in **/*cpp; do
grep -qm 1 Foo "$file" &&
(grep -A 3 Foo "$file" | grep -q Bar || echo "$file");
done