sed: So fügen Sie den ersten x Vorkommen Text hinzu

sed: So fügen Sie den ersten x Vorkommen Text hinzu

Ich versuche, bei den ersten x-maligen Vorkommen Text an das Ende einer Zeile anzufügen. Ich weiß, wie das global und beim n-ten Vorkommen geht. Ich weiß nicht, wie das beim ersten n-ten Vorkommen geht. Ein Beispiel wäre eine text.txt-Datei, die Folgendes enthält:

This is a test
junk
This is a test
More junk
This is a test
This is a test
This is a test

Und ich möchte am Ende der ersten drei Male, in denen „Dies ist ein Test“ vorkommt, einen „.“ hinzufügen. Die Ausgabe, die ich erhalten möchte, ist:

This is a test.
junk
This is a test.
More junk
This is a test.
This is a test
This is a test

Antwort1

This.*testist der richtige reguläre Ausdruck. Das Sternchen bedeutet „0 oder mehr Mal das vorherige Zeichen“ und This*testwürde daher auf keine Ihrer Zeilen zutreffen.

Nun, Sed ist schlecht in Arithmetik. Für etwas Elegantes schlage ich Awk vor:

awk '/This.*test/{c++};{print $0 (c<4 ? "." : "")}' file

Ich denke, es genügt zu sagen c, dass wie jede nicht gesetzte Variable in Awk als Null behandelt wird, aber lassen Sie es mich wissen, wenn Sie weitere Erläuterungen benötigen.

Antwort2

Eine weitere Variante, die das Ausführen des Regexp-Abgleichs vermeidet, nachdem alle 3 Vorkommen bereits gefunden wurden:

awk -v n=3 'n && /This is a test/ {n--; $0 = $0 "."}; {print}'

Mit sed„spezifisch“ könnten Sie beispielsweise Folgendes tun:

sed '
  1 {
    x
    s/^/.../
    x
  }
  /This is a test/ {
    s/$/./
    x
    s/.//
    /./ {
      x
      b
    }
    g
    :1
    $! {
      n
      b 1
    }
  }'

Dabei verfolgen wir die Anzahl der .anzuhängenden s als entsprechende Anzahl von .s im Haltebereich.

Es versteht sich von selbst, dass dies sedfür diese Art von Aufgabe viel weniger geeignet ist. Wenn der Grund für den Wunsch seddie -iErweiterung für die direkte Bearbeitung ist, die in einigen Implementierungen zu finden ist (ausgeliehen von perl), beachten Sie, dass die GNU-Implementierung von awkdies auch mit ¹ kann -i /usr/share/awk/inplace.awk, oder Sie können das Original verwenden:

perl -lpi -e '
  if ($n < 3 && /This is a test/) {
    $n++;
    $_ .= ".";
  }' your-file

.Wenn Sie nach jedem Vorkommen von This is a testund nicht nach allen Zeilen, die mindestens ein Vorkommen von enthalten, ein hinzufügen möchten This is a test, perlwäre auch die beste Wahl:

perl -pi -e 's{This is a test\K}{$n++ < 3 ? "." : ""}ge' your-file

¹verwende nicht-i inplaceas versucht zunächst, die Erweiterung (as oder ) aus dem aktuellen Arbeitsverzeichnis gawkzu laden , wo jemand Malware platziert haben könnte. Der Pfad der mit gelieferten Erweiterung kann je nach System unterschiedlich sein, siehe die Ausgabe voninplaceinplaceinplace.awkinplacegawkgawk 'BEGIN{print ENVIRON["AWKPATH"]}'

Antwort3

Mit perlkönnten wir tun, wie gezeigt

perl -lpe '
  $_ = $k == 3 ? next : s/This is a test(?{$k++}).*\K/./r;
' file

Elefanten können auch tanzen, allerdings nur in einfachen Schritten. Wenn wir GNU sedim erweiterten Regex-Modus schreiben, -E können wir die Anzahl als Anzahl der Zeilenumbrüche im Hold speichern.

K=3
sed -Ee '
  /This is a test/!b
  G
  /(.*\n){'"$K"'}.*\n/!{
    s/\n+/./p;z;H;d
  }
  s/\n+//
  :a;n;ba
' file

verwandte Informationen