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.

Oli
źródło
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 ', ...

Oli
źródło
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
Amit
źródło
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

ayaz
źródło
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:

bukzor
źródło
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 
Oli
źródło
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
8

Z wyszukiwarką srebra :

ag 'abc.*(\n|.)*efg'

Optymalizacje szybkości poszukiwacza srebra mogłyby tu zabłysnąć.

Shwaydogg
źródło
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.

svent
źródło
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

albfan
źródło
@ Ɖ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
Jaskółka oknówka
źródło
2
perl -ne 'print if (/begin pattern/../end pattern/)' filename
pbal
źródło
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).

kenorb
źródło