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.*test
ist der richtige reguläre Ausdruck. Das Sternchen bedeutet „0 oder mehr Mal das vorherige Zeichen“ und This*test
wü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 sed
für diese Art von Aufgabe viel weniger geeignet ist. Wenn der Grund für den Wunsch sed
die -i
Erweiterung für die direkte Bearbeitung ist, die in einigen Implementierungen zu finden ist (ausgeliehen von perl
), beachten Sie, dass die GNU-Implementierung von awk
dies 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 test
und nicht nach allen Zeilen, die mindestens ein Vorkommen von enthalten, ein hinzufügen möchten This is a test
, perl
wäre auch die beste Wahl:
perl -pi -e 's{This is a test\K}{$n++ < 3 ? "." : ""}ge' your-file
¹verwende nicht-i inplace
as versucht zunächst, die Erweiterung (as oder ) aus dem aktuellen Arbeitsverzeichnis gawk
zu laden , wo jemand Malware platziert haben könnte. Der Pfad der mit gelieferten Erweiterung kann je nach System unterschiedlich sein, siehe die Ausgabe voninplace
inplace
inplace.awk
inplace
gawk
gawk 'BEGIN{print ENVIRON["AWKPATH"]}'
Antwort3
Mit perl
kö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 sed
im 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