Kann ich eine Datei durch Ausschneiden ändern?

17

Die manSeite gibt mir nicht viel Hoffnung, aber ich hoffe, es ist eine undokumentierte (und / oder GNU-spezifische) Funktion.

Hank Gay
quelle

Antworten:

14

Das kannst du nicht. Verwenden Sie entweder ed oder GNU sed oder perl oder tun Sie das, was Sie hinter den Kulissen tun: Erstellen Sie eine neue Datei für den Inhalt.

ed, tragbar:

ed foo <<EOF
1,$s/^\([^,]*\),\([^,]*\),\([^,]*\).*/\1,\3/
w
q
EOF

GNU sed:

sed -i -e 's/^\([^,]*\),\([^,]*\),\([^,]*\).*/\1,\3/' foo

Perl:

perl -i -l -F, -pae 'print @F[1,3]' foo

cut, Erstellen einer neuen Datei (empfohlen, da Sie Ihr Skript bei einer Unterbrechung einfach erneut ausführen können):

cut -d , -f 1,3 <foo >foo.new &&
mv -f foo.new foo

cut, Ersetzen der Datei an Ort und Stelle (behält das Eigentum und die Berechtigungen von foo, muss aber vor Unterbrechungen geschützt werden):

cp -f foo foo.old &&
cut -d , -f 1,3 <foo.old >foo &&
rm foo.old

Ich empfehle eine der cut-basierten Methoden. Auf diese Weise sind Sie nicht auf ein nicht standardmäßiges Tool angewiesen. Sie können das beste Tool für den Job verwenden und das Verhalten bei Unterbrechungen steuern.

Gilles 'SO - hör auf böse zu sein'
quelle
Besser als .oldMethode für Änderungen an Ort und Stelle,echo "$(cut -d , -f 1,3 <foo)" > foo
GypsyCosmonaut
1
@GypsyCosmonaut Nein, das ist nicht "besser". Es ist zerbrechlicher. Der einzige Vorteil ist, dass die Eingabe kürzer ist. Das Hauptproblem bei Ihrer Methode besteht darin, dass die Daten verloren gehen, wenn beim Verarbeiten der Eingabedatei oder beim Schreiben der Ausgabe ein Fehler auftritt. Es funktioniert auch nicht mit Binärdaten: Die Ausgabe wird beim ersten Null-Byte abgeschnitten. Auch bei Textdateien werden leere Zeilen am Ende der Datei entfernt. Bei großen Dateien kann dies fehlschlagen, da die Daten als Zeichenfolge im Speicher der Shell gespeichert werden müssen (und denken Sie daran, dass die Daten in diesem Fall verloren gehen).
Gilles 'SO- hör auf böse zu sein'
oO Danke, ich wusste nicht, dass es Probleme mit null Byte geben könnte
GypsyCosmonaut
Ich denke, das ist einfacher:cut -d , -f 1,3 foo > foo.new rm foo mv foo.new foo
LoMaPh
@LoMaPh In der Tat weiß ich nicht, warum ich die alte Datei umbenannt habe: Es hat keine Vorteile gegenüber dem Umbenennen der neuen Datei. Es ist auch einfacher, weil Sie den Schritt nicht brauchen rm foo. Und Sie sollten nicht anrufen rm foo, weil mv foo.new fooes atomar ist: Es entfernt die alte Version und setzt gleichzeitig die neue Version ein.
Gilles 'SO - hör auf böse zu sein'
23

Das moreutils- Paket von Ubuntu (und auch Debian ) hat ein Programm sponge, das sozusagen auch Ihr Problem löst.

Vom mann schwamm:

Schwamm liest die Standardeingabe und schreibt sie in die angegebene Datei. Im Gegensatz zu einer Shell-Umleitung nimmt der Schwamm alle Eingaben auf, bevor die Ausgabedatei geöffnet wird. Auf diese Weise können Pipelines eingeschränkt werden, die aus derselben Datei lesen und in dieselbe Datei schreiben.

Welches würde Sie etwas tun lassen wie:

cut -d <delim> -f <fields> somefile | sponge somefile
Kjetil Jorgensen
quelle
9

Ich denke nicht, dass das cutalleine möglich ist . Ich konnte es auf der Man- oder Infoseite nicht finden. Sie können so etwas tun wie

mytemp=$(mktemp) && cut -d" " -f1 file > $mytemp && mv $mytemp file

mktempmacht Sie zu einer relativ sicheren temporären Datei, in die Sie die cutAusgabe umleiten können.

Steven D
quelle
1

Versuchen Sie vim-way:

$ ex -s +'%!cut -c 1-10' -cxa file.txt

Dadurch wird die Datei direkt bearbeitet (so wird zuerst die Sicherung durchgeführt).

Alternativ verwenden grep, sedoder gawk.

Kenorb
quelle
0

Sie können slurp mit POSIX Awk verwenden:

cut -b1 file | awk 'BEGIN{RS="";getline<"-";print>ARGV[1]}' file

Beispiel

Steven Penny
quelle
0

Nun, da es cutweniger ausgibt als es liest, können Sie Folgendes tun:

cut -c1 < file 1<> file

Dies bedeutet, dass die Standardeingabe fileim schreibgeschützten Modus und die Standardeingabe fileim schreibgeschützten Modus ohne Kürzung ( <>) geöffnet wird .

Auf diese Weise cutwird nur die Datei über sich selbst überschrieben. Der Rest der Datei bleibt jedoch unberührt. Zum Beispiel, wenn fileenthält:

foo
bar

Die Ausgabe wird zu:

f
b
bar

Die f\nb\nhaben ersetzt foo\n, sind aber barnoch da. Sie müssen die Datei nach cutAbschluss abschneiden .

Mit ksh93können Sie dies mit dem <>;Operator tun, der sich so <>verhält, als ob bei erfolgreichem Befehl ftruncate()der Dateideskriptor aufgerufen wird. So:

cut -c1 < file 1<>; file

Bei anderen Shells müssten Sie das auf ftruncate()anderem Wege tun , wie zum Beispiel:

{ cut -c1 < file; perl -e 'truncate STDOUT, tell STDOUT';} 1<> file

perlNur dafür aufzurufen , ist hier allerdings etwas übertrieben, vor allem, wenn man bedenkt, dass perldies leicht gelingen kann cutwie:

perl -pi -e '$_ = substr($_, 0, 1)' file

Beachten Sie, dass bei allen Methoden, die ein tatsächliches Umschreiben vor Ort beinhalten, eine beschädigte Datei angezeigt wird, wenn der Vorgang auf halbem Weg unterbrochen wird. Die Verwendung einer temporären zweiten Datei vermeidet dieses Problem.

Stéphane Chazelas
quelle