grep, aby znaleźć wystąpienia „Foo”, w których „Bar” nie pojawia się w ciągu 10 linii


10

Załóżmy, że chcę przeszukać całe drzewo w poszukiwaniu wszystkich plików CPP, w których występuje „Foo”. Mogę zrobić:

find . -name "*.cpp" | xargs grep "Foo"

Załóżmy teraz, że chcę wymienić tylko te przypadki, w których jakiś inny ciąg, powiedzmy, że „Bar” nie występuje w ciągu 3 wierszy poprzedniego wyniku.

Biorąc pod uwagę dwa pliki:

a.cpp

1 Foo
2 qwerty
3 qwerty

b.cpp

1 Foo
2 Bar
3 qwerty

Chciałbym skonstruować proste wyszukiwanie, w którym znaleziono „Foo” z a.cpp, ale „Foo” z b.cpp nie.

Czy istnieje sposób na osiągnięcie tego w dość prosty sposób?


Może rozwiązaniem może być opcja grep -A i / lub grep -B i / lub grep -C. Próbuję, ale bez
— powodzenia

@ maurelio79: Moja obecna teoria jest taka. Grep dla „Foo” za pomocą -A 10 dla kontekstu. Potokuj do grep -v Bar. Pipe to sed, aby uzyskać nazwę pliku i numer linii. Pipe to (coś?), Aby wydrukować tę linię.
— John Dibling

Odpowiedzi:


17

Z pcregrep:

pcregrep --include='\.cpp$' -rnM 'Foo(?!(?:.*\n){0,2}.*Bar)' .

Klucz znajduje się w -Mopcji, która jest unikalna pcregrepi służy do dopasowania wielu linii ( pcregrepw razie potrzeby pobiera więcej danych z pliku wejściowego, gdy wymaga tego chodzenie po RE).

(?!...)jest operatorem RE o negatywnym spojrzeniu w przyszłość perl / PCRE. Foo(?!...)dopasowuje Footak długo, jak ...nie pasuje do tego, co następuje.

...jest (?:.*\n){0,2}.*Bar( .nie pasuje do znaku nowej linii), to jest od 0 do 2 linii, po których następuje linia zawierająca Bar.


+1: doskonale. Dzięki wielkie; Jestem pewien, że nie było łatwo znaleźć poprawne wyrażenie regularne. Bardzo doceniam twoje wysiłki. To wydaje się działać dokładnie tak, jak chciałem.
— John Dibling

2
Boczne pytanie, jeśli chcesz odpowiedzieć. Skąd się dowiedziałeś pcregrep? Nigdy wcześniej o tym nie słyszałem.
— John Dibling

@JohnDibling osobiście dowiedziałem się niedawno na unix.SE . Ta RE nie jest szczególnie skomplikowana, zwłaszcza gdy znasz operatora RE o (?!...)negatywnych perlprognozach.
— Stéphane Chazelas

9

Nieważne, po prostu użyj pcregrepzgodnie 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ć -Aprzełącznika grep do wyprowadzania dopasowanych linii i N kolejnych linii. Następnie przekazujesz wynik przez a, grep Bara 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.cppjest zwracane, mimo że zawiera, Barponieważ linia z Barzawiera 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

+1 schludne Nieco bardziej skomplikowane, niż się spodziewałem, ale wcale nie takie złe.
— John Dibling

Zakłada się, że „Foo” występuje tylko raz. Spowoduje to również zgłoszenie plików, które nie zawierają Foo. Brakuje cytatów.
— Stéphane Chazelas

@StephaneChazelas dzięki, cytaty naprawione. Masz rację, jeśli chodzi o zgłaszanie plików bez, Fooi naprawiłem to, ale nie widzę twojego zdania na temat wielu przypadków Foo. Powinien sobie z nimi poradzić poprawnie.
— terdon

@JohnDibling zobacz aktualizacje.
— terdon

1
Nie zgłosi pliku zawierającego 100 wierszy „Foo”, a po nim „Bar”.
— Stéphane Chazelas

0

Nie testowałem, rozmawiam przez telefon:

find . -name "*.cpp" | xargs awk '/foo/{t=$0;c=10}/bar/{c=0;t=""}c{c--}t&&!c{print t;t=""}END&&t{print t}' 

coś w tym stylu.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.