it-swarm.com.de

Wie greife ich nach mehreren Mustern in mehreren Zeilen?

Um genau zu sein

Some text
begin
Some text goes here.
end
Some more text

und ich möchte einen ganzen Block extrahieren, der von "begin" bis "end" beginnt.

mit awk können wir awk '/begin/,/end/' text machen.

Wie mache ich mit grep?

18
Iker

Aktualisiert am 18.11.2016 (da das Grep-Verhalten geändert wurde: Grep mit -P-Parameter unterstützt jetzt nicht mehr ^ und $ anchors [unter Ubuntu 16.04 mit Kernel v: 4.4.0-21-generic]) ( falsches (nicht-) Update )

$ grep -Pzo "begin(.|\n)*\nend" file
begin
Some text goes here.  
end

hinweis: Für andere Befehle ersetzen Sie einfach die Anker '^' & '$' durch Anker mit neuer Zeile '\n' ______________________________

Mit grep Befehl:

grep -Pzo "^begin\$(.|\n)*^end$" file

Wenn Sie die Muster "begin" und "end" nicht in das Ergebnis aufnehmen möchten, verwenden Sie grep mit Lookbehind- und Lookahead-Unterstützung.

grep -Pzo "(?<=^begin$\n)(.|\n)*(?=\n^end$)" file

Sie können auch \K notify anstelle von Lookbehind assertion verwenden.

grep -Pzo "^begin$\n\K(.|\n)*(?=\n^end$)" file

\K Option ignoriert alles vor dem Mustervergleich und ignoriert das Muster selbst.
\n wird verwendet, um das Drucken von Leerzeilen aus der Ausgabe zu vermeiden.

Oder, wie @AvinashRaj andeutet, gibt es einfach leicht grep wie folgt:

grep -Pzo "(?s)^begin$.*?^end$" file

grep -Pzo "^begin\$[\s\S]*?^end$" file

(?s) weist grep an, zuzulassen, dass der Punkt mit Zeilenumbrüchen übereinstimmt.
[\s\S] stimmt mit jedem Zeichen überein, bei dem es sich entweder um Leerzeichen oder Nicht-Leerzeichen handelt.

Und ihre Ausgabe ohne "begin" und "end" ist wie folgt:

grep -Pzo "^begin$\n\K[\s\S]*?(?=\n^end$)" file # or grep -Pzo "(?<=^begin$\n)[\s\S]*?(?=\n^end$)"

grep -Pzo "(?s)(?<=^begin$\n).*?(?=\n^end$)" file

Den vollständigen Test aller Befehle finden Sie hier (veraltet, da grep-Verhalten mit -P-Parameter geändert wird)

Hinweis:

^ zeigt auf den Anfang einer Linie und $ zeigt auf das Ende einer Linie. Diese werden zu den Übereinstimmungen "Anfang" und "Ende" hinzugefügt, wenn sie alleine in einer Zeile stehen.
In zwei Befehlen bin ich $ entkommen, weil ich auch "Command Substitution" ($(command)) verwende, mit der die Ausgabe eines Befehls den Befehlsnamen ersetzen kann.

Vom mann grep:

-o, --only-matching
      Print only the matched (non-empty) parts of a matching line,
      with each such part on a separate output line.

-P, --Perl-regexp
      Interpret PATTERN as a Perl compatible regular expression (PCRE)

-z, --null-data
      Treat the input as a set of lines, each terminated by a zero byte (the ASCII 
      NUL character) instead of a newline. Like the -Z or --null option, this option 
      can be used with commands like sort -z to process arbitrary file names.
13
αғsнιη

Falls Ihr grep die Perl-Syntax (-P) nicht unterstützt, können Sie versuchen, die Linien zu verbinden, das Muster anzupassen und die Linien dann wie folgt erneut zu erweitern:

$ tr '\n' , < foo.txt | grep -o "begin.*end" | tr , '\n'
begin
Some text goes here.
end
2
kenorb