Wie kann ich "grep -zoP" dazu bringen, jedes Spiel separat anzuzeigen?

Nov 23 2020

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 -zin 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

2 tripleee Nov 23 2020 at 20:23

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 -zund (meistens) grep -P, also ist das vielleicht akzeptabel.

5 Sundeep Nov 23 2020 at 20:16

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+foobazfü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
4 anubhava Nov 23 2020 at 22:21

Hier ist ein anderer Gnu-awk Lösung Gebrauch macht von RSund 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
3 EdMorton Nov 23 2020 at 21:51

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 Xkeine Nichtwiederholung ist regexp metachar ( zum Beispiel ., ^, [, etc.) so YMMV

1 rowboat Nov 24 2020 at 10:09

GNU grep -zbeendet 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

-onumberanstelle von Lookarounds verwendet. ?Lazy Quantifier hinzugefügt (falls \1später auftritt).