Kann jemand einen eleganten Weg vorschlagen, um dies zu erreichen?
Eingang:
test instant ()
test instant ()
...
test instant () //total 1000 lines
Ausgabe sollte sein:
test instant1 ()
test instant2 ()
test instant1000()
Die leeren Zeilen befinden sich in meinen Eingabedateien und es gibt viele Dateien im selben Verzeichnis, die ich gleichzeitig verarbeiten muss.
Ich habe versucht, viele Dateien im selben Verzeichnis zu ersetzen, aber es hat nicht funktioniert.
for file in ./*; do perl -i -000pe 's/instance$& . ++$n/ge' "$file"; done
Fehler:
Substitution replacement not terminated at -e line 1.
Substitution replacement not terminated at -e line 1.
und ich habe es auch versucht: perl -i -pe 's/instant/$& . ++$n/ge' *.vs
Es hat funktioniert, aber der Index wurde immer weiter von einer Datei zur anderen erhöht. Ich möchte das für diff-Datei auf 1 zurücksetzen. irgendwelche guten Vorschläge?
find . -type f -exec perl -pi -e 's/instant/$& . ++$n{$ARGV}/ge' {} +
funktioniert, aber es ersetzt alle anderen Dateien sollte nicht ersetzt werden. Ich ziehe es vor, die Dateien nur durch "* .txt" zu ersetzen.
test instant ()
?Antworten:
oder mit GNU
awk
:Fügen Sie die
-i
Option hinzu, um die Dateien direkt zu bearbeitenperl
:Oder rekursiv:
Erklärungen
-p
besteht darin, die Eingabe zeilenweise zu verarbeiten, den-e
für jede Zeile übergebenen Ausdruck auszuwerten und auszudrucken. Für jede Zeile ersetzen wir (mit dems/re/repl/flags
Operator) sichinstant
selbst ($&
) und den inkrementierten Wert einer Variablen++$n
. Dieg
Flagge ist die Substitution global (nicht nur einmal) zu machen, unde
so , dass der Ersatz als perl - Code zu interpretieren ist e bewerten (keine fester string).Für die direkte Bearbeitung, bei der ein Perl-Aufruf mehr als eine Datei verarbeitet, möchten wir
$n
jede Datei zurücksetzen. Stattdessen verwenden wir$n{$ARGV}
(wo$ARGV
ist die aktuell verarbeitete Datei).Der
awk
verdient eine Erklärung.Wir nutzen die Fähigkeit von GNU
awk
, Datensätze in beliebigen Zeichenfolgen (sogar regulären Ausdrücken) zu trennen. Mit setzen-vRS=instant
wir den Aufzeichnungsabscheider aufinstant
.RT
ist die Variable, die enthältRS
,instant
womit normalerweise übereinstimmt , mit Ausnahme des letzten Datensatzes, in dem es sich um die leere Zeichenfolge handelt. In der obigen Eingabe sind die Datensätze ($0
) und Datensatzterminatoren (RT
) ([$0|RT]
):Wir müssen also zu Beginn jedes Datensatzes mit Ausnahme des ersten Datensatzes eine inkrementelle Zahl einfügen.
Welches ist, was wir oben tun. Für den ersten Datensatz
n
wird leer sein. Wir setzen ORS (den Ausgangsaufzeichnungsspeicher ) auf RT, damitawk
gedruckt wirdn $0 RT
. Dies geschieht mit dem zweiten Ausdruck (++n
), der eine Bedingung ist, die immer als wahr ausgewertet wird (eine Zahl ungleich Null), und daher wird die Standardaktion (Drucken$0 ORS
) für jeden Datensatz ausgeführt.quelle
sed
ist wirklich nicht das beste Werkzeug für den Job, Sie möchten etwas mit besseren Skriptfunktionen. Hier sind einige Möglichkeiten:Perl
Das
-p
bedeutet "jede Zeile drucken", nachdem das angegebene Skript angewendet wurde-e
. Der-000
"Absatzmodus" wird aktiviert, sodass Datensätze (Zeilen) durch aufeinanderfolgende Zeilenumbrüche (\n
) definiert werden. Dadurch können Zeilen mit doppeltem Abstand korrekt behandelt werden.$&
ist das letzte übereinstimmende Muster und$.
die aktuelle Zeilennummer der Eingabedatei. Mit deme
ins///e
kann ich Ausdrücke im Substitutionsoperator auswerten.awk (dies setzt voraus, dass Ihre Daten genau wie gezeigt sind, mit drei durch Leerzeichen getrennten Feldern)
Hier erhöhen wir die
k
Variablek
nur, wenn die aktuelle Zeile nicht leer ist././
In diesem Fall drucken wir auch die erforderlichen Informationen. Leere Zeilen werden unverändert gedruckt.verschiedene Muscheln
Hier wird jede Eingabezeile automatisch in Leerzeichen aufgeteilt und die Felder werden als
$a
,$b
und gespeichert$c
. Dann wird innerhalb der Schleife$c
für jede Zeile, für die$a
nicht leer ist, um eins erweitert, und der aktuelle Wert wird neben dem zweiten Feld gedruckt$b
.HINWEIS: Bei allen oben genannten Lösungen wird davon ausgegangen, dass alle Zeilen in der Datei dasselbe Format haben. Wenn nicht, ist die Antwort von @ Stephane der richtige Weg.
Um mit vielen Dateien umzugehen und davon auszugehen, dass Sie dies für alle Dateien im aktuellen Verzeichnis tun möchten , können Sie Folgendes verwenden:
ACHTUNG: Das setzt voraus , einfache Dateinamen ohne Leerzeichen, wenn nötig mit etwas komplexen, geht für (unter der Annahme umgehen
ksh93
,zsh
oderbash
):quelle