Jak mogę wyszukać wzór wielowierszowy w pliku?


128

Musiałem znaleźć wszystkie pliki, które zawierały określony wzór ciągu. Pierwszym rozwiązaniem, które przychodzi na myśl, jest użycie find piped z xargs grep :

find . -iname '*.py' | xargs grep -e 'YOUR_PATTERN'

Ale jeśli muszę znaleźć wzory, które obejmują więcej niż jedną linię, utknąłem, ponieważ grep waniliowy nie może znaleźć wzorów wielowierszowych.



2
Ten jest starszy, więc powiedziałbym, że to nie jest duplikat :)
rogerdpack

@rogerdpack Przy oznaczaniu pytań jako duplikatów wiek pytania jest kwestią trzeciorzędną, po ilości i jakości odpowiedzi oraz jakości pytania.
tripleee

Odpowiedzi:


98

Więc odkryłem pcregrep, co oznacza GREP kompatybilne z Perl .

Na przykład, musisz znaleźć pliki, w których po zmiennej „ _name ” bezpośrednio następuje zmienna „ _description ”:

find . -iname '*.py' | xargs pcregrep -M '_name.*\n.*_description'

Wskazówka: we wzorze musisz uwzględnić znak końca wiersza. W zależności od platformy może to być '\ n', \ r ',' \ r \ n ', ...


7
Jak wspomniano w halka poniżej, „możesz także przekonać symbol wieloznaczny z kropką, aby dopasowywał nowe linie, jeśli dodasz (?) Do wyrażenia regularnego”. Następnie użyj grep z wyrażeniem regularnym perl, dodając -P. odnaleźć . -exec grep -nHP '(? s) WYBIERZ. {1,60} OD. {1,20} nazwa_tabeli' '{}' \;
Jim

8
pcregrepjest dostępny na komputerach Mac zbrew install pcre
Jared Beck

1
Nawet lepiej: również używać -Hktóry drukuje nazwę pliku przed każdym meczem: pcregrep -HM.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

97

Dlaczego nie wybierzesz awk :

awk '/Start pattern/,/End pattern/' filename

2
Jest to znacznie łatwiejsze do zrozumienia i zastosowania, awkktóre występuje w większości systemów * nix.
Ali Karbassi

24
miły! czy jest sposób, aby ten mecz nie był chciwy?
marcin

3
W jaki sposób wydrukowałbyś nazwę pliku tylko wtedy, gdy istnieje dopasowanie?
bibstha

2
Możesz pokazać numery wierszy dopasowań za pomocą awk '/Start pattern/,/End pattern/ {printf NR " "; print}' filename. Można zrobić to ładniej nadając numery linii o stałej szerokości awk '/Start pattern/,/End pattern/ {printf "%-4s ", NR; print}' filename.
Robert

Wydaje się, że działa to dobrze na jednym pliku, ale co jeśli chciałbym wyszukiwać w wielu plikach?
Jinstrong

84

Oto przykład użycia GNUgrep :

grep -Pzo '_name.*\n.*_description'

-z/ --null-dataTraktuj dane wejściowe i wyjściowe jako sekwencje linii.

Zobacz także tutaj


1
Myślę, że to odpowiada tylko za jeden znak nowej linii.
Chmura

1
Nie byłem w stanie użyć grep do wyszukiwania wielowierszowego bez użycia flag, -zwięc nie dzieli wyszukiwania na jedną linię i -owyświetla tylko dopasowaną część.
bbaja42

Okazało się, że -o spowodowało, że nic nie drukowało, ale grep -rzl pattern *-pracowałem, aby uzyskać listę plików (moje polecenie brzmiało , -rzo nie działało)
Benubird

5
Polecam '' grep -Pazo '' zamiast '' -Pzo '' dla plików innych niż ASCII. Jest to lepsze, ponieważ przełącznik -z w plikach innych niż ASCII może wywołać zachowanie „danych binarnych” grepa, które zmienia zwracane wartości. Przełącz '' -a | --text '' zapobiega temu.
rloth

Nie działa na komputerach Mac z zainstalowanym git przezbrew reinstall --with-pcre git
Quanlong

21

grep -Prównież używa libpcre, ale jest znacznie szerzej instalowana. Aby znaleźć pełną titlesekcję dokumentu HTML, nawet jeśli obejmuje on wiele wierszy, możesz użyć tego:

grep -P '(?s)<title>.*</title>' example.html

Ponieważ projekt PCRE implementuje standard Perl, skorzystaj z dokumentacji Perla jako odniesienia:


Hmm próbowałem tego właśnie teraz i wydawało się, że nie działa ... gist.github.com/rdp/0286d91624930bd11d0169d6a6337c33
rogerdpack

Nie wiedziałem, że grep ma taką opcję. Prawdopodobnie z tego powodu: jest to wysoce eksperymentalne i grep -P może ostrzegać o niezaimplementowanych funkcjach. ; to jest pod CentOS 7. Pod Fedorą 29: to jest eksperyment, a grep -P może ostrzegać o niezaimplementowanych funkcjach . Oczywiście w BSD grep nie ma go wcale. Byłoby miło, gdyby nie było tak eksperymentalne, ale miło jest o tym przypominać - mało, chociaż prawdopodobnie go użyję.
Pryftan

17

Oto bardziej przydatny przykład:

pcregrep -Mi "<title>(.*\n){0,5}</title>" afile.html

Przeszukuje tag tytułu w pliku html, nawet jeśli obejmuje do 5 wierszy.

Oto przykład nieograniczonej liczby linii:

pcregrep -Mi "(?s)<title>.*</title>" example.html 

4
dzięki za to. Utknąłem, nie zdając sobie sprawy, że symbol wieloznaczny nie pasuje do znaku nowej linii.
mat.

7
@matt: możesz również przekonać symbol wieloznaczny z kropką do dopasowania do nowych linii, jeśli dodasz (?s)do wyrażenia regularnego, na przykład:"(?s)<html>.*</html>"
lubomir.brindza

@matt Oczywiście możesz zaznaczyć $(na końcu wzoru), aby zaznaczyć, że to koniec linii - chociaż to nie to samo, co pomoc w znalezieniu wielu wzorów linii. Zobacz także glob(7). Możesz również znaleźć tę stronę internetową, która Cię interesuje: regular-expressions.info
Pryftan


4

Możesz użyć alternatywnego przesiewania grep tutaj (zastrzeżenie: jestem autorem).

Obsługuje dopasowywanie wielowierszowe i ogranicza wyszukiwanie do określonych typów plików po wyjęciu z pudełka:

sift -m --files '* .py' 'TWÓJ_WZÓR'

(przeszukaj wszystkie pliki * .py pod kątem określonego wielowierszowego wzorca wyrażenia regularnego)

Jest dostępny dla wszystkich głównych systemów operacyjnych. Spójrz na stronę próbek, aby zobaczyć, jak można jej użyć do wyodrębnienia wartości wielowierszowych z pliku XML.


3

Ta odpowiedź może być przydatna:

Regex (grep) potrzebny do wyszukiwania wieloliniowego

Aby znaleźć rekursywnie, możesz użyć flag -R (rekurencyjne) i --include (wzorzec GLOB). Widzieć:

Użyj składni grep --exclude / - include, aby nie przeszukiwać niektórych plików


@ Ɖiamond ǤeezeƦ zwróć uwagę, że edycja posta w LQP ( stackoverflow.com/review/low-quality-posts/19341146 ) unieważnia recenzję, więc po prostu edytuj, jeśli masz pewność, że post wymaga utrzymania.
fedorqui 'SO przestać szkodzić'

2

@Marcin: awk przykład non-chciwy:

awk '{if ($0 ~ /Start pattern/) {triggered=1;}if (triggered) {print; if ($0 ~ /End pattern/) { exit;}}}' filename

2
perl -ne 'print if (/begin pattern/../end pattern/)' filename

Spowoduje to jednak wydrukowanie całego pliku
Herbert

1

Użycie opcji ex/ vieditor i globstar (składnia podobna do awki sed):

ex +"/string1/,/string3/p" -R -scq! file.txt

gdzie aaajest Twój punkt początkowy i bbbkońcowy tekst.

Aby wyszukiwać rekurencyjnie, spróbuj:

ex +"/aaa/,/bbb/p" -scq! **/*.py

Uwaga: Aby włączyć **składnię, uruchom shopt -s globstar(Bash 4 lub zsh).

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.