Jak mogę grepować katalog na podstawie zawartości dwóch kolejnych wierszy?

11

Jak mogę grepować katalog dla linii zawierających „Foo”, ale dopasowywać tylko, gdy następny wiersz zawiera również „Bar”?

Nathan Long
źródło
Problem jest teraz zupełnie inny niż oryginał: / Może lepiej cofnąć stare wersje i POST inną? Co więcej, nowe pytanie nie jest dla mnie jasne.
Gilles Quenot,
@sputnick - jak to możliwe? Podałem katalog, kiedy po raz pierwszy opublikowałem pytanie; Pogrubiłem to tylko dlatego, że ludzie nie zauważyli.
Nathan Long,
Nieważne, to zadziała, odpowiednio zmodyfikuję mój test POST.
Gilles Quenot

Odpowiedzi:

7

@ warl0ck wskazał mi właściwy kierunek pcregrep, ale powiedziałem: „zawiera”, a nie „jest” i zapytałem o katalog, a nie plik.

Wydaje mi się, że to działa.

pcregrep -rMi 'Foo(.*)\n(.*)Bar' .
Nathan Long
źródło
6

Wydaje się, że sam Grep go nie obsługuje, zamiast tego użyj pcregrep:

Foo
Bar
Foo
abc

pcregrep -M "Foo\nBar" file

Dostał:

Foo
Bar
stokrotka
źródło
3
OP tego nie powiedział Fooi Barbędzie obejmować całą linię.
tojrobinson
6

Ze sedskryptem:

#!/bin/sed -nf

/^Foo/{
    h         # put the matching line in the hold buffer
    n         # going to nextline
    /^Bar/{   # matching pattern in newline
        H     # add the line to the hold buffer
        x     # return the entire paragraph into the pattern space
        p     # print the pattern space
        q     # quit the script now
    }
}

Aby go użyć:

chmod +x script.sed
printf '%s\n' * | ./script.sed

printfTutaj zobaczyć wszystkie pliki w bieżącym katalogu w jednym wierszu każdego, i przekazać je sed.

Uwaga : jest to posortowane według kolejności alfabetycznej.

Więcej informacji przydatnych pattern spacei hold space TUTAJ .

grymoire.com ma naprawdę dobre rzeczy na temat shellprogramowania.

Gilles Quenot
źródło
Co h, n, H, x, p, qznaczy Bardzo interesujące.
Yamaneko
Zobacz moje komentarze. Inne informacje na temat pattern space& hold space: grymoire.com/Unix/Sed.html#uh-56 lub francuskim commentcamarche.net/faq/9536-sed-introduction-a-sed-part-i
Gilles Quenot
POST przystosowany do pracy nad katalogiem
Gilles Quenot
4

Używając greptylko, możesz zbudować następujący potok:

grep -A1 'Foo' input_file | grep -B1 'Bar' | grep 'Foo'

Pierwszy greppobierze wszystkie linie, które zawierają, Fooa także linię po meczu. Następnie otrzymujemy linie, które zawierają, Barjak również linię przed dopasowaniem, i na koniec wyodrębniamy linie z tego wyjścia, które zawierają Foo.

EDYCJA: Jak wskazano manatwork , istnieją pewne problematyczne przypadki, których należy przestrzegać. Chociaż jest to interesujące wyzwanie, z uwagi grepna funkcjonalność zorientowaną na linię, każde rozwiązanie z tym rozwiązaniem może być „włamaniem” i prawdopodobnie lepiej jest użyć czegoś, pcregrepco bardziej pasuje do danego zadania.

tojrobinson
źródło
Miły. Zapytałem jednak o katalog; wydaje się, że to działa:find . -name '*.txt' | xargs grep -A1 'Foo' | grep -B1 'Bar'
Nathan Long,
Spowoduje to również wyświetlenie wystąpień z „Foo” i „Bar” w tej samej linii.
manatwork
@manatwork: Linie zawierające „Foo” i „Bar” są „liniami zawierającymi„ Foo ”, czyli o to pytano.
tojrobinson
1
@tojrobinson, a co z „ale otrzymuj dopasowania tylko wtedy, gdy następny wiersz zawiera także część„ Bar ”? pastebin.com/Yj8aeCEA
manatwork
3

Chociaż wolę używać rozwiązania Nathana pcregrep, oto rozwiązanie wykorzystujące tylko grep

grep -o -z -P  'Foo(.*)\n(.*)Bar' file

Objaśnienie opcji:

  • -owydrukuj tylko pasującą część. Konieczne, ponieważ dołączenie -zspowoduje wydrukowanie całego pliku (chyba że gdzieś jest \ 0)
  • -z Traktuj dane wejściowe jako zestaw wierszy zakończonych zerowym bajtem (znak NUL ASCII) zamiast nowego wiersza.
  • -P składnia wyrażeń regularnych perl

EDYCJA: Ta wersja drukuje całe dopasowane linie

    grep -o -P -z  '(.*)Foo(.*)\n(.*)Bar(.*)' file
bbaja42
źródło
1
Fajna sztuczka co -z. Niektóre „(. *)” Przed i po całym wyrażeniu spowodowałyby, że wypisałby całe dopasowane linie. Na razie podciągi przed „Foo” i po „Bar” nie są wyświetlane.
manatwork
1

Z awk:

awk '/bar/ && prev != "" {print FILENAME ": " prev "\n" FILENAME ": " $0}
     /foo/ {prev=$0; next}
     {prev=""}' file1...

(ogólna uwaga na temat ograniczenia awk: uwaga, że ​​jeśli niektóre nazwy plików mogą zawierać znaki „=”, musisz przekazać je jako ./filenamezamiast filenameawk)

Stéphane Chazelas
źródło