Wie kann ich in einer Datei nach einem mehrzeiligen Muster suchen?

127

Ich musste alle Dateien finden, die ein bestimmtes Zeichenfolgenmuster enthielten. Die erste Lösung, die mir in den Sinn kommt, ist die Verwendung von find mit xargs grep :

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

Aber wenn ich Muster finden muss, die sich über mehr als eine Zeile erstrecken, stecke ich fest, weil Vanilla Grep keine mehrzeiligen Muster finden kann.

Oli
quelle
2
Dieser ist älter, also würde ich sagen, dass es kein Duplikat ist :)
Rogerdpack
@rogerdpack Wenn Sie Fragen als Duplikate markieren, ist das Alter einer Frage nach der Anzahl und Qualität der Antworten und der Qualität der Frage ein tertiäres Problem.
Tripleee

Antworten:

98

Also entdeckte ich pcregrep, was für Perl Compatible Regular Expressions GREP steht .

Beispielsweise müssen Sie Dateien suchen, in denen die Variable ' _name ' unmittelbar gefolgt von der Variablen ' _description ' ist:

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

Tipp: Sie müssen das Zeilenumbruchzeichen in Ihr Muster aufnehmen. Abhängig von Ihrer Plattform kann es sich um '\ n', \ r ',' \ r \ n ', ... handeln.

Oli
quelle
7
Wie von halka unten erwähnt, "können Sie den Punkt-Platzhalter auch davon überzeugen, mit Zeilenumbrüchen übereinzustimmen, wenn Sie Ihrem regulären Ausdruck (? S) hinzufügen". Verwenden Sie dann grep mit Perl-Regex, indem Sie -P hinzufügen. finden . -exec grep -nHP '(? s) SELECT. {1,60} FROM. {1,20} Tabellenname' '{}' \;
Jim
8
pcregrepist auf dem Mac verfügbar mitbrew install pcre
Jared Beck
1
Noch besser: Verwenden Sie auch, -Hwelche den Dateinamen vor jedem Spiel druckt : pcregrep -HM.
Ciro Santilli 法轮功 冠状 病 六四 事件 21
97

Warum gehst du nicht für awk :

awk '/Start pattern/,/End pattern/' filename
Amit
quelle
2
Dies ist viel einfacher zu verstehen und wird awkbei den meisten * nix-Systemen verwendet.
Ali Karbassi
24
nett! Gibt es eine Möglichkeit, dieses Match nicht gierig zu machen?
Marcin
3
Wie würden Sie den Dateinamen nur drucken, wenn eine Übereinstimmung vorliegt?
Bibstha
2
Sie können die Zeilennummern der Übereinstimmungen mit anzeigen awk '/Start pattern/,/End pattern/ {printf NR " "; print}' filename. Sie können es schöner machen, indem Sie den Zeilennummern eine feste Breite geben : awk '/Start pattern/,/End pattern/ {printf "%-4s ", NR; print}' filename.
Robert
Dies scheint bei einer einzelnen Datei gut zu funktionieren. Was ist jedoch, wenn ich in mehreren Dateien suchen möchte?
Jinstrong
83

Hier ist das Beispiel mit GNUgrep :

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

-z/ --null-dataBehandeln Sie Eingabe- und Ausgabedaten als Zeilenfolgen.

Siehe auch hier

Ayaz
quelle
1
Ich denke, das macht nur einen einzigen Zeilenumbruch aus.
Wolke
1
Ich konnte grep nicht für die mehrzeilige Suche verwenden, ohne Flags zu verwenden -z, damit die Suche nicht in eine einzelne Zeile aufgeteilt wird und -onur übereinstimmende Teile gedruckt werden.
bbaja42
Ich fand, dass -o dazu führte, dass nichts gedruckt wurde, aber -l arbeitete, um eine Liste der Dateien zu erhalten (mein Befehl war grep -rzl pattern *, -rzo funktionierte nicht)
Benubird
5
Ich empfehle '' grep -Pazo '' anstelle von '' -Pzo '' für Nicht-ASCII-Dateien. Es ist besser, weil der Schalter -z bei Nicht-ASCII-Dateien das Verhalten von grep "Binärdaten" auslösen kann, das die Rückgabewerte ändert. Schalter '' -a | --text '' verhindert das.
10.
Funktioniert nicht auf Mac mit Git installiert vonbrew reinstall --with-pcre git
Quanlong
21

grep -Pverwendet auch libpcre, ist aber viel weiter installiert. Um einen vollständigen titleAbschnitt eines HTML-Dokuments zu finden, können Sie Folgendes verwenden, auch wenn es mehrere Zeilen umfasst:

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

Da das PCRE-Projekt den Perl-Standard implementiert, verwenden Sie die Perl-Dokumentation als Referenz:

Bukzor
quelle
Hmm versuchte dies gerade und schien nicht zu funktionieren ... gist.github.com/rdp/0286d91624930bd11d0169d6a6337c33
Rogerdpack
Ich wusste nicht, dass grep diese Option hat. Wahrscheinlich aus diesem Grund: Dies ist sehr experimentell und grep -P kann vor nicht implementierten Funktionen warnen. ;; Das ist unter CentOS 7. Unter Fedora 29: Dies ist experimentell und grep -P warnt möglicherweise vor nicht implementierten Funktionen . Natürlich ist es in BSD grep überhaupt nicht da. Wäre schön, wenn es nicht so experimentell wäre, aber es ist schön, daran erinnert zu werden - wenig, obwohl ich es wahrscheinlich verwenden werde.
Pryftan
17

Hier ist ein nützlicheres Beispiel:

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

Es durchsucht das Titel-Tag in einer HTML-Datei, auch wenn es bis zu 5 Zeilen umfasst.

Hier ist ein Beispiel für unbegrenzte Zeilen:

pcregrep -Mi "(?s)<title>.*</title>" example.html 
Oli
quelle
4
Danke dafür. Mir war nicht klar, dass ein Platzhalter nicht zum Zeilenumbruchzeichen passen würde.
Matt
7
@matt: Sie können den Punkt-Platzhalter auch davon überzeugen, mit Zeilenumbrüchen übereinzustimmen, wenn Sie (?s)Ihrem regulären Ausdruck Folgendes hinzufügen :"(?s)<html>.*</html>"
lubomir.brindza
@matt Natürlich können Sie nach $(am Ende eines Musters) suchen, um anzuzeigen , dass es das Ende der Zeile ist - obwohl dies nicht das Gleiche ist, wie wenn Sie mehrere Zeilenmuster finden. Siehe auch glob(7). Sie könnten auch diese Website von Interesse finden: regulär-expressions.info
Pryftan
8

Mit Silbersucher :

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

Geschwindigkeitsoptimierungen von Silver Searcher könnten hier möglicherweise glänzen.

Shwaydogg
quelle
4

Sie können hier das alternative Grep-Sift verwenden (Haftungsausschluss: Ich bin der Autor).

Es unterstützt den mehrzeiligen Abgleich und beschränkt die Suche sofort auf bestimmte Dateitypen:

sift -m --files '* .py' 'YOUR_PATTERN'

(Durchsuchen Sie alle * .py-Dateien nach dem angegebenen mehrzeiligen regulären Ausdrucksmuster.)

Es ist für alle gängigen Betriebssysteme verfügbar. Werfen Sie einen Blick auf die Proben - Seite , um zu sehen , wie es verwendet werden kann , um mehrzeilige Werte aus einer XML - Datei zu extrahieren.

svent
quelle
3

Diese Antwort könnte nützlich sein:

Regex (grep) für mehrzeilige Suche erforderlich

Um rekursiv zu suchen, können Sie die Flags -R (rekursiv) und --include (GLOB-Muster) verwenden. Sehen:

Verwenden Sie die Syntax grep --exclude / - include, um bestimmte Dateien nicht zu durchsuchen

Albfan
quelle
@ Ɖiamond ǤeezeƦ Beachten Sie, dass das Bearbeiten eines Beitrags im LQP ( stackoverflow.com/review/low-quality-posts/19341146 ) die Überprüfung ungültig macht. Bearbeiten Sie sie einfach, wenn Sie sicher sind, dass der Beitrag beibehalten werden muss.
Fedorqui 'SO hör auf zu schaden'
2

@Marcin: awk Beispiel nicht gierig:

awk '{if ($0 ~ /Start pattern/) {triggered=1;}if (triggered) {print; if ($0 ~ /End pattern/) { exit;}}}' filename
Martin
quelle
2
perl -ne 'print if (/begin pattern/../end pattern/)' filename
pbal
quelle
Dies druckt jedoch die gesamte Datei
Herbert
1

Verwenden der Optionex / vieditor und globstar (Syntax ähnlich wie awkund sed):

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

Wo aaaist Ihr Startpunkt und bbbIhr Endtext?

Um rekursiv zu suchen, versuchen Sie:

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

Hinweis: Um die **Syntax zu aktivieren , führen Sie shopt -s globstar(Bash 4 oder zsh) aus.

Kenorb
quelle