Nieważne, po prostu użyj pcregrep
zgodnie z sugestią @StephaneChazelas.
To powinno działać:
$ find . -name "*.cpp" |
while IFS= read -r file; do
grep -A 3 Foo "$file" | grep -q Bar || echo "$file";
done
Chodzi o to, aby użyć -A
przełącznika grep do wyprowadzania dopasowanych linii i N kolejnych linii. Następnie przekazujesz wynik przez a, grep Bar
a jeśli to nie pasuje (wyjście> 0), to echo nazwy pliku.
Jeśli wiesz, że masz rozsądne nazwy plików (bez spacji, nowych wierszy lub innych dziwnych znaków), możesz uprościć:
$ for file in $(find . -name "*.cpp"); do
grep -A 3 Foo "$file" | grep -q Bar || echo "$file";
done
Na przykład:
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
Zwróć uwagę, że c.cpp
jest zwracane, mimo że zawiera, Bar
ponieważ linia z Bar
zawiera więcej niż 3 linie po Foo
. Możesz kontrolować liczbę wierszy, które chcesz wyszukać, zmieniając wartość przekazywaną do -A
:
$ for file in $(find . -name "*.cpp"); do
grep -A 10 Foo "$file" | grep -q Bar || echo "$file";
done
./a.cpp
Oto krótszy (zakładając, że używasz bash
):
$ shopt -s globstar
$ for file in **/*cpp; do
grep -A 10 Foo "$file" | grep -q Bar || echo "$file";
done
WAŻNY
Jak zauważył Stephane Chazelas w komentarzach, powyższe rozwiązania będą również drukować pliki, które w ogóle nie zawierają Foo
. Ten pozwala uniknąć:
for file in **/*cpp; do
grep -qm 1 Foo "$file" &&
(grep -A 3 Foo "$file" | grep -q Bar || echo "$file");
done