Speichern Sie Änderungen mit awk

135

Ich lerne awkund möchte wissen, ob es eine Option zum Schreiben von Änderungen in eine Datei gibt, ähnlich wie sedbei einer -iOption zum Speichern von Änderungen an einer Datei.

Ich verstehe, dass ich die Umleitung verwenden könnte, um Änderungen zu schreiben. Gibt es jedoch eine Möglichkeit awk, dies zu tun?

Deano
quelle
Siehe auch serverfault.com/a/547331/313521 für die allgemeinere Antwort auf "Bearbeiten einer vorhandenen Datei mit Umleitung".
Wildcard
@ Wildcard. Die Lösung dort ist schrecklich zerbrechlich. Es gibt absolut keine Garantie für die Reihenfolge von Ereignissen, und die Verwendung dieser Lösung kann dazu führen, dass Ihre Daten abgeschnitten werden. Abgesehen davon kann ich diese Site nicht direkt kommentieren, da ich dafür 50 Mitarbeiter auf dieser Site benötige. Ich werde nie verstehen, warum SO in Unix / Linux und Server Admin et al. IMO, das war ein Fehler.
William Pursell
@WilliamPursell, "keine Garantie für die Reihenfolge von Ereignissen" - das ist eigentlich falsch. Die einzige Fragilität dieser Lösung besteht darin, dass die Länge des Inhalts größer als die maximale Länge eines Befehls ist. Die Reihenfolge der Veranstaltungen ist jedoch garantiert.
Wildcard
@Wildcard Welcher Standard garantiert diese Bestellung?
William Pursell
@ WilliamPursell es wird durch die Bash-Dokumentation garantiert. Für andere Muscheln weiß ich nicht. (Übrigens, wenn Sie Ihr Konto verknüpfen, haben Sie 100 Assoziationsbonus und können Kommentare abgeben.)
Wildcard

Antworten:

141

In der neuesten Version von GNU Awk (seit der Veröffentlichung von 4.1.0 ) besteht die Möglichkeit, Dateien an Ort und Stelle zu bearbeiten :

[...] Die mit der neuen Funktion erstellte "Inplace" -Erweiterung kann zur Simulation der GNU- sed -iFunktion verwendet werden. [...]

Anwendungsbeispiel:

$ gawk -i inplace '{ gsub(/foo/, "bar") }; { print }' file1 file2 file3

So behalten Sie das Backup:

$ gawk -i inplace -v INPLACE_SUFFIX=.bak '{ gsub(/foo/, "bar") }
> { print }' file1 file2 file3
Lind
quelle
1
@sudo_O - Danke für die "Inplace" -Demonstration. Hat deine Antwort positiv bewertet!
Lind
Sieht aus wie die Option möglicherweise entfernt wurde? Mit 4.1.3 habe ich "-i includefile --include = includefile"
Keith Hughitt
1
@ Keith Ich hatte die gleiche Frage. Ich habe es gerade versucht und es funktioniert auf meinem 4.1.3. inplaceist tatsächlich eine Bibliothek, die gawkgemäß der Antwort von iiSeymour enthalten ist , also inplaceetwas, das als enthalten sein kann includefile.
cxw
Eine wichtige Einschränkung hier: Das 'Seen'-Array wird mit doppelten Zeilen aus ALLEN im Befehl enthaltenen Dateien gefüllt. Wenn also jede Datei zB einen gemeinsamen Header hat, wird dieser in jeder Datei nach der ersten entfernt. Wenn Sie stattdessen jede Datei einzeln behandeln möchten, müssen Sie etwas wie für f in * .txt tun. gawk -i inplace '! Seen [$ 0] ++' "$ f"; fertig
Nick K9
135

Es sei denn, Sie haben GNU awk 4.1.0 oder höher ...

Sie haben keine Option wie die von sed. -iTun Sie stattdessen Folgendes:

$ awk '{print $0}' file > tmp && mv tmp file

Hinweis: Das -iist keine Zauberei, es wird auch eine temporäre Datei erstellt, die sednur für Sie erstellt wird.


Ab GNU awk 4.1.0 ...

GNU awkDiese Funktionalität wurde in Version 4.1.0 (veröffentlicht am 10/05/2013) hinzugefügt . Es ist nicht so einfach, nur die -iOption zu geben, wie in den veröffentlichten Anmerkungen beschrieben:

Die neue Option -i (von xgawk) wird zum Laden von awk-Bibliotheksdateien verwendet. Dies unterscheidet sich von -f darin, dass das erste Argument ohne Option als Skript behandelt wird.

Sie müssen die mitgelieferte inplace.awkInclude-Datei verwenden, um die Erweiterung ordnungsgemäß aufzurufen:

$ cat file
123 abc
456 def
789 hij

$ gawk -i inplace '{print $1}' file

$ cat file
123
456
789

Mit der Variablen INPLACE_SUFFIXkann die Erweiterung für eine Sicherungsdatei angegeben werden:

$ gawk -i inplace -v INPLACE_SUFFIX=.bak '{print $1}' file

$ cat file
123
456
789

$ cat file.bak
123 abc
456 def
789 hij

Ich bin froh, dass diese Funktion hinzugefügt wurde, aber für mich ist die Implementierung nicht sehr schwierig, da die Leistung von der Prägnanz der Sprache herrührt und imo-i inplace 8 Zeichen zu lang ist .

Hier ist ein Link zum Handbuch für das offizielle Wort.

Chris Seymour
quelle
Sollte Ihr "erstes" Beispiel nicht eher so aussehen : awk '{ gsub(/foo/, "bar" ) } ; { print $0 }' file > tmp.txt && mv -v tmp.txt file?
Tony Barganski
Zu meiner Überraschung, ab April 2019, immer noch bei Gawk 4.0.2. Lassen Sie sich von niemandem sagen, dass eine solche Version verfügbar sein wird.
John Lunzer
Litte kürzer awk '{print $0}' file | sponge filemit spongeab moreutils.
Brablc
15

@sudo_O hat die richtige Antwort .

Das kann nicht funktionieren:

someprocess < file > file

Die Shell führt die Umleitungen durch, bevor sie die Kontrolle an einen Prozess übergibt ( Umleitungen ). Durch die >Umleitung wird die Datei auf die Größe Null gekürzt ( Umleitung der Ausgabe ). Daher gibt es zum Zeitpunkt des Starts eines Prozesses, der aus der Datei lesen möchte, keine Daten zum Lesen.

Glenn Jackman
quelle
14

Nur ein kleiner Hack, der funktioniert

echo "$(awk '{awk code}' file)" > file
Yuri G.
quelle
Klappt wunderbar! Aber ist es möglich, den Befehl awk in einer Variablen zu speichern und ihn einfach in Ihrem raffinierten Trick zu verwenden?
Ashrasmun
12

Eine Alternative ist zu verwenden sponge:

awk '{print $0}' your_file | sponge your_file

Wo Sie '{print $0}'durch Ihr awk-Skript und your_filedurch den Namen der Datei ersetzen, die Sie an Ort und Stelle bearbeiten möchten.

sponge absorbiert die Eingabe vollständig, bevor sie in der Datei gespeichert wird.

Codoskop
quelle
Wie Standard / tragbar ist Schwamm?
Thomas
2
spongeist ein Teil von moreutils. Daher ist es in den meisten Systemen nicht standardmäßig vorhanden. Aber es sieht so aus, als ob zumindest spongeselbst tragbar genug ist und fast überall ausgeführt werden kann.
MarSoft
1
Der Nachteil dieser Lösung im Vergleich zu tee-basiert ist, dass spongevor dem Aufschreiben alles in den Arbeitsspeicher gelesen wird und daher große Dateien eingefroren werden.
MarSoft
5

Folgendes wird nicht funktionieren

echo $(awk '{awk code}' file) > file

das sollte funktionieren

echo "$(awk '{awk code}' file)" > file
Flowmix Leonsio
quelle
3

Falls Sie eine Nur-AWK-Lösung wünschen, ohne eine temporäre Datei zu erstellen und mit Version! = (Gawk 4.1.0) verwendbar zu sein:

awk '{a[b++]=$0} END {for(c=0;c<=b;c++)print a[c]>ARGV[1]}' file
Falke
quelle
4
Aber puffert dies die gesamte Datei in den Speicher? Betrachten Sie eine 20-GB-Datei.
Amit Naidu
0

Mit Tee

 awk '{awk code}' file | tee file

Der teeBefehl wird ausgeführt und ausgeführt, nachdem der awkBefehl aufgrund des Befehls beendet wurde |.

Shaiki Siegal
quelle
5
Das ist falsch. Die beiden Befehle werden parallel ausgeführt und die Daten werden sofort über die Pipe gestreamt. Jede Datei, die größer als der Puffer ist (8192 Byte auf meinem Computer), wird abgeschnitten und Sie verlieren Daten.
Tripflag