Wie greife ich nach mehreren Mustern in mehreren Zeilen?

19

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 gerne machen awk '/begin/,/end/' text.

Wie mache ich mit grep?

Iker
quelle
2
Gleiche Frage unter Unix & Linux . Tu das nicht .
Gilles 'SO - hör auf böse zu sein'

Antworten:

14

Aktualisiert am 18. November 2016 (da das Grep-Verhalten geändert wurde: Grep mit -P-Parameter wird jetzt nicht unterstützt ^und $Anker [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 \Knotify anstelle von Lookbehind Assertion verwenden.

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

\KOption ignoriere alles vor dem Mustervergleich und ignoriere das Muster selbst.
\nDient zum Vermeiden des Druckens leerer Zeilen aus der Ausgabe.

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, den Punkt mit Zeilenumbrüchen abzustimmen.
[\s\S]Stimmt mit jedem Zeichen überein, das entweder ein Leerzeichen oder ein Nicht-Leerzeichen ist.

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 das Grep-Verhalten mit dem -P-Parameter geändert wurde ).

Hinweis:

^Zeigen Sie auf den Anfang einer Linie und $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 geflüchtet, $weil es auch "Command Substitution" ( $(command)) verwendet, 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.
αғsнιη
quelle
Ändern Sie Ihr Grep grep -Pzo "(?<=begin\n)(.|\n)*(?=\nend)" file, um \nZeichen, die in der Zeile vorhanden sind , nicht zu drucken .
Avinash Raj
Verwenden Sie den Modifikator DOTALL, um den Punkt so zu machen, dass er auch mit Zeilenumbrüchen übereinstimmtgrep -Pzo "(?s)begin.*?end" file
Avinash Raj,
Oder einfachgrep -Pzo "begin[\s\S]*?end" file
Avinash Raj
1
Die Lösung funktioniert nicht. Es kommt zu einem Fehler: grep: ein nicht geschütztes ^ oder $ wird mit -Pz nicht unterstütztDie Übersetzung des Fehlers grep: a not protected ^ or $ is not supported with -Pz
sieht ungefähr
1
Ja, ich weiß, das ist deine Antwort. Ich bin mir sicher, dass es geklappt hat, als Sie dies gepostet haben, aber versuchen Sie es heute noch einmal. Das Verhalten von grepscheint sich geändert zu haben.
Terdon
2

Falls Sie grepdie Perl-Syntax ( -P) nicht unterstützen , 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
Kenorb
quelle