Wie kann ich "grep -zoP" dazu bringen, jedes Spiel separat anzuzeigen?
Ich habe eine Datei auf diesem Formular:
X/this is the first match/blabla
X-this is
the second match-
and here we have some fluff.
Und ich möchte alles extrahieren, was nach "X" und zwischen denselben Markierungen erscheint. Wenn ich also "X + match +" habe, möchte ich "match" erhalten, da es nach "X" und zwischen den Markierungen "+" erscheint.
Für die angegebene Beispieldatei möchte ich diese Ausgabe haben:
this is the first match
und dann
this is
the second match
Ich habe es geschafft, den gesamten Inhalt zwischen X gefolgt von einem Marker zu erhalten, indem ich Folgendes verwendet habe:
grep -zPo '(?<=X(.))(.|\n)+(?=\1)' file
Das ist:
grep -Po '(?<=X(.))(.|\n)+(?=\1)'
X zuzuordnen(something)
, gefolgt von dem , wird erfasst und am Ende mit abgeglichen(?=\1)
(ich habe den Code auf meine Antwort hier gestützt ).- Hinweis: Ich verwende
(.|\n)
, um alles abzugleichen, einschließlich einer neuen Zeile, und das verwende ich auch-z
in grep, um auch neue Zeilen abzugleichen.
Das funktioniert also gut, das einzige Problem kommt von der Anzeige der Ausgabe:
$ grep -zPo '(?<=X(.))(.|\n)+(?=\1)' file
this is the first matchthis is
the second match
Wie Sie sehen können, werden alle Übereinstimmungen zusammen angezeigt, gefolgt von "Dies ist die erste Übereinstimmung", gefolgt von "Dies ist die zweite Übereinstimmung" ohne Trennzeichen. Ich weiß, dass dies auf die Verwendung von "-z" zurückzuführen ist, das die gesamte Datei als eine Reihe von Zeilen behandelt, die jeweils durch ein Null-Byte (das ASCII-NUL-Zeichen) anstelle einer neuen Zeile (unter Angabe von "man grep") abgeschlossen werden.
Also: Gibt es eine Möglichkeit, alle diese Ergebnisse separat zu erhalten?
Ich habe es auch in GNU Awk versucht:
awk 'match($0, /X(.)(\n|.*)\1/, a) {print a[1]}' file
aber nicht einmal das hat (\n|.*)
funktioniert.
Antworten
Der Anwendungsfall ist problematisch, da Sie beim Drucken der Übereinstimmungen die Information darüber verlieren, wo genau sich das Trennzeichen befand. Aber wenn das akzeptabel ist, versuchen Sie es mit Rohrleitungen xargs -r0
.
grep -zPo '(?<=X(.))(.|\n)+(?=\1)' file | xargs -r0
Diese Optionen sind GNU-Erweiterungen, aber das ist es auch grep -z
und (meistens) grep -P
, also ist das vielleicht akzeptabel.
awk
unterstützt keine Rückreferenzen innerhalb der Regexp-Definition.
Problemumgehungen:
$ grep -zPo '(?s)(?<=X(.)).+(?=\1)' ip.txt | tr '\0' '\n' this is the first match this is the second match # with ripgrep, which supports multiline matching $ rg -NoUP '(?s)(?<=X(.)).+(?=\1)' ip.txt
this is the first match
this is
the second match
Kann auch (?s)X(.)\K.+(?=\1)
anstelle von verwenden (?s)(?<=X(.)).+(?=\1)
. Möglicherweise möchten Sie hier auch einen nicht gierigen Quantifizierer verwenden, um eine Übereinstimmung match+xyz+foobaz
für eine Eingabe zu vermeidenX+match+xyz+foobaz+
Mit perl
$ perl -0777 -nE 'say $& while(/X(.)\K.+(?=\1)/sg)' ip.txt
this is the first match
this is
the second match
Hier ist ein anderer Gnu-awk Lösung Gebrauch macht von RS
und RT
:
awk -v RS='X.' 'ch != "" && n=index($0, ch) { print substr($0, 1, n-1)
}
RT {
ch = substr(RT, 2, 1)
}' file
this is the first match
this is
the second match
Mit GNU awk für Multi-Char RS, RT und gensub () und ohne die gesamte Datei in den Speicher lesen zu müssen:
$ awk -v RS='X.' 'NR>1{print "<" gensub(end".*","",1) ">"} {end=substr(RT,2,1)}' file
<this is the first match>
<this is
the second match>
Offensichtlich habe ich "<" und ">" hinzugefügt, damit Sie sehen können, wo jeder Ausgabedatensatz beginnt / endet.
Das setzt voraus , daß über das Zeichen , nachdem X
keine Nichtwiederholung ist regexp metachar ( zum Beispiel .
, ^
, [
, etc.) so YMMV
GNU grep -z
beendet Eingabe- / Ausgabedatensätze mit Nullzeichen (nützlich in Verbindung mit anderen Tools wie sort -z
). pcregrep wird das nicht tun:
pcregrep -Mo2 '(?s)X(.)(.+?)\1' file
-onumber
anstelle von Lookarounds verwendet. ?
Lazy Quantifier hinzugefügt (falls \1
später auftritt).