Es scheint, ich missbrauche grep
/ egrep
.
Ich habe versucht, in mehreren Zeilen nach Zeichenfolgen zu suchen und konnte keine Übereinstimmung finden, obwohl ich weiß, dass das, wonach ich suche, übereinstimmen sollte. Ursprünglich dachte ich, dass meine regulären Ausdrücke falsch waren, aber ich las schließlich, dass diese Werkzeuge pro Zeile funktionieren (auch meine regulären Ausdrücke waren so trivial, dass es nicht das Problem sein konnte).
Welches Tool würde man also verwenden, um Muster über mehrere Zeilen hinweg zu suchen?
grep
. Sie sind eng miteinander verwandt, aber keine Trottel, IMO."grep"
das Verb "to grep" vorschlägt und Top-Antworten, einschließlich der akzeptierten, grep nicht verwenden.Antworten:
Hier ist eine
sed
, die Ihnengrep
über mehrere Zeilen hinweg ein ähnliches Verhalten verleiht :Wie es funktioniert
-n
Unterdrückt das Standardverhalten beim Drucken jeder Zeile/foo/{}
instruiert sie übereinstimmenfoo
mit den entsprechenden Linien und das zu tun , was im Inneren der squigglies kommt. Ersetzen Siefoo
durch den Anfangsteil des Musters.:start
ist ein Verzweigungslabel, das uns hilft, die Schleife zu durchlaufen, bis wir das Ende unseres regulären Ausdrucks gefunden haben./bar/!{}
führt aus, was in den Schnörkeln zu den Linien ist, die nicht zusammenpassenbar
. Ersetzen Siebar
durch den Endteil des Musters.N
fügt die nächste Zeile an den aktiven Puffer an (sed
nennt dies den Musterraum)b start
verzweigt bedingungslos zu demstart
zuvor erstellten Etikett, um die nächste Zeile anzuhängen, solange der Musterbereich nichts enthältbar
./your_regex/p
druckt den Musterbereich, wenn er übereinstimmtyour_regex
. Sie solltenyour_regex
durch den gesamten Ausdruck ersetzen, den Sie über mehrere Zeilen hinweg abgleichen möchten.quelle
sed: 1: "/foo/{:start /bar/!{N;b ...": unexpected EOF (pending }'s)
sed: unterminated {
Fehlersed
Implementierungen zu tun hat . Ich habe versucht, den Empfehlungen in dieser Antwort zu folgen, um das obige Skript standardkonform zu machen, aber es hat mir gesagt, dass "start" eine undefinierte Bezeichnung ist. Ich bin mir also nicht sicher, ob dies auf standardkonforme Weise möglich ist. Wenn Sie es schaffen, können Sie meine Antwort gerne bearbeiten.Ich benutze im Allgemeinen ein Tool namens ,
pcregrep
die in den meisten Linux - Geschmack installiert werden kann , mityum
oderapt
.Zum Beispiel.
Angenommen, Sie haben eine Datei mit dem Namen
testfile
contentSie können den folgenden Befehl ausführen:
Pattern Matching über mehrere Zeilen durchführen.
Darüber hinaus können Sie dasselbe auch mit
sed
.quelle
Hier ist ein einfacherer Ansatz mit Perl:
oder (da JosephR den
sed
Weg eingeschlagen hat , werde ich seinen Vorschlag schamlos stehlen )Erläuterung
$f=join("",<>);
: liest die gesamte Datei und speichert deren Inhalt (Zeilenumbrüche und alles) in der Variablen$f
. Wir versuchen dann, eine Übereinstimmung zufoo\nbar.*\n
finden und drucken sie, falls sie übereinstimmt (die spezielle Variable$&
enthält die zuletzt gefundene Übereinstimmung). Das///m
wird benötigt, damit der reguläre Ausdruck über Zeilenumbrüche hinweg übereinstimmt.Die
-0
Einstellung der Eingangsdatensatztrennzeichen . Wenn Sie diese00
Option aktivieren, wird der Absatzmodus aktiviert, in dem Perl aufeinanderfolgende Zeilenumbrüche (\n\n
) als Datensatztrennzeichen verwendet. In Fällen, in denen keine aufeinanderfolgenden Zeilenumbrüche vorhanden sind, wird die gesamte Datei auf einmal gelesen (geschlürft).Warnung:
Sie nicht tun dies für große Dateien, wird die gesamte Datei in den Speicher geladen werden und das kann ein Problem sein.
quelle
Eine Möglichkeit, dies zu tun, ist Perl. zB hier ist der Inhalt einer Datei mit dem Namen
foo
:Hier ist etwas Perl, das mit jeder Zeile, die mit foo beginnt, gefolgt von jeder Zeile, die mit bar beginnt, übereinstimmt:
Das Perl, aufgeschlüsselt:
while(<>){$all .= $_}
Dadurch wird die gesamte Standardeingabe in die Variable geladen$all
while($all =~
Während die Variableall
den regulären Ausdruck hat .../^(foo[^\n]*\nbar[^\n]*\n)/m
Der reguläre Ausdruck: foo am Zeilenanfang, gefolgt von einer beliebigen Anzahl von Nicht-Zeilenumbrüchen, gefolgt von einem Zeilenumbruch, unmittelbar gefolgt von "bar" und dem Rest der Zeile mit einem Balken./m
am Ende des regulären Ausdrucks bedeutet "Übereinstimmung über mehrere Zeilen"print $1
Gibt den in Klammern gesetzten Teil des regulären Ausdrucks aus (in diesem Fall den gesamten regulären Ausdruck).s/^(foo[^\n]*\nbar[^\n]*\n)//m
Löschen Sie die erste Übereinstimmung für den regulären Ausdruck, damit mehrere Fälle des regulären Ausdrucks in der betreffenden Datei übereinstimmen könnenUnd die Ausgabe:
quelle
perl -n0777E 'say $& while /^foo.*\nbar.*\n/mg' foo
Der grep alternative sift unterstützt Multiline Matching (Disclaimer: Ich bin der Autor).
Angenommen,
testfile
enthält:sift -m '<description>.*?</description>'
(zeige die Zeilen mit der Beschreibung)Ergebnis:
sift -m '<description>(.*?)</description>' --replace 'description="$1"' --no-filename
(Beschreibung extrahieren und neu formatieren)Ergebnis:
quelle
Einfach ein normaler grep, der
Perl-regexp
parameter unterstützt, erledigtP
diese Aufgabe.(?s)
DOTALL-Modifikator, der in Ihrem Regex einen Punkt erzeugt, der nicht nur den Zeichen, sondern auch den Zeilenumbrüchen entspricht.quelle
-P
OptionIch habe dieses Problem mit grep und der Option -A mit einem anderen grep gelöst.
Die Option -A 1 gibt 1 Zeile nach der gefundenen Zeile aus. Natürlich hängt es von Ihrer Datei- und Wortkombination ab. Für mich war es die schnellste und zuverlässigste Lösung.
quelle
Angenommen, wir haben die Datei test.txt mit:
Der folgende Code kann verwendet werden:
Für die folgende Ausgabe:
quelle
Wenn wir den Text zwischen den 2 Mustern erhalten möchten, schließen Sie sich aus.
Angenommen, wir haben die Datei test.txt mit:
Der folgende Code kann verwendet werden:
Für die folgende Ausgabe:
Wie es funktioniert, machen wir es Schritt für Schritt
/foo/{
wird ausgelöst wenn line "foo" enthältn
Ersetzen Sie den Musterraum durch die nächste Zeile, dh das Wort "hier"b gotoloop
Abzweig zum Label "gotoloop":gotoloop
definiert das Label "gotoloop"/bar/!{
wenn das Muster keinen "Balken" enthälth
Ersetzen Sie den Laderaum durch ein Muster, damit "hier" im Laderaum gespeichert wirdb loop
Abzweig zum Label "loop":loop
definiert das Label "loop"N
Hängt das Muster an den Laderaum an.Nun enthält hold space:
"here"
"ist das"
:gotoloop
Wir sind jetzt bei Schritt 4 und schleifen, bis eine Zeile "bar" enthält./bar/
Endlosschleife ist beendet, "Balken" wurde gefunden, es ist der Musterraumg
Der Pattern Space wird durch einen Hold Space ersetzt, der alle Zeilen zwischen "foo" und "bar" enthält, die während der Hauptschleife gespeichert wurdenp
Kopieren Sie den Musterbereich in die StandardausgabeGetan !
sed mehrzeilige Schleife
quelle