Ich habe heute versucht, schnell eine .hgignore
Datei aus der Cygwin-Bash-Shell zu bearbeiten , und eine Zeile hinzugefügt, die ein Fehler war. Ich bin mir nicht sicher, ob dies der beste Weg ist, aber ich dachte schnell darüber head -1 .hgignore
nach, die fehlerhafte Zeile zu entfernen (ich hatte zuvor nur eine Zeile in der Datei). Sicher genug, wenn es ausgeführt wird, gibt es die erste Zeile als einzige Ausgabe.
Aber als ich versuchte, die Ausgabe umzuleiten und die Datei mit neu zu schreiben head -1 .hgignore > .hgignore
, war die Datei leer. Warum passiert das? Wenn ich stattdessen das Anhängen versuche, head -1 .hgignore >> .hgignore
wird es korrekt angehängt, aber dies ist offensichtlich nicht das gewünschte Ergebnis. Warum funktioniert eine Kürzungsumleitung in diesem Fall nicht?
quelle
cut
eine Datei an Ort und Stelle ändern? , Wie kann ich dafür sorgen, dass iconv die Eingabedatei durch die konvertierte Ausgabe ersetzt?Antworten:
Wenn die Shell eine Befehlszeile wie folgt erhält:
command > file.out
Die Shell selbst öffnet (und erstellt möglicherweise) die benannte Dateifile.out
. Die Shell setzt den Dateideskriptor 0 auf den Dateidateideskriptor, den sie vom Öffnen erhalten hat. So funktioniert die E / A-Umleitung: Jeder Prozess kennt die Dateideskriptoren 0, 1 und 2.Der schwierige Teil dabei ist, wie man öffnet
file.out
. Meistens möchten Siefile.out
mit Offset 0 zum Schreiben geöffnet (dh abgeschnitten), und dies hat die Shell für Sie getan. Es hat .hgignore abgeschnitten, zum Schreiben geöffnet, den Dateiskriptor auf 0 getäuscht und dann ausgeführthead
. Sofortiges Überladen von Dateien.In der Bash-Shell
set noclobber
ändern Sie dieses Verhalten.quelle
Ich denke, Bruce antwortet mit der Shell-Pipeline, was hier los ist .
Eines meiner kleinen Lieblingsprogramme ist der
sponge
Befehl von moreutils . Es löst genau dieses Problem, indem es alle verfügbaren Eingaben "aufnimmt", bevor es die Zielausgabedatei öffnet und die Daten schreibt. Damit können Sie Pipelines genau so schreiben, wie Sie es erwartet haben:Die Lösung für den armen Mann besteht darin, die Ausgabe in eine temporäre Datei weiterzuleiten. Nachdem die Pipline ausgeführt wurde (z. B. der nächste Befehl, den Sie ausführen), wird die temporäre Datei wieder an den ursprünglichen Speicherort der Datei verschoben.
quelle
head -1 .hgignore | tee .hgignore
?tee
ist in coreutils, und als Vorteil / Nebeneffekt schreibt dies auch an STDOUTtee
wird die Datei, in die geschrieben wird, geöffnet und abgeschnitten, wenn sie wie alles andere instanziiert wird, sodass das Hauptproblem der Race-Bedingung beim Lesen des Dateiinhalts vor dem Abschneiden beim Schreiben nicht gelöst wird.tee
scheint das Gewünschte zu tun. Ich habe eine Version8.13
auf meinem Computer.tee
Programm schneidet Ihre Dateien ab, es ist nicht so eingerichtet, dass sie doppelt gepuffert werden.Im
file
wird abgeschnitten, bevorhead
gestartet wird, aber wenn Sie es schreiben:Es ist nicht so, wie
file
es im Lese- / Schreibmodus geöffnet ist. Wenn Sie mit demhead
Schreiben fertig sind, wird die Datei jedoch nicht abgeschnitten, sodass die obige Zeile ein No-Op ist (head
schreiben Sie einfach die erste Zeile über sich selbst und lassen Sie die anderen Zeilen unberührt).Nach
head
der Rückkehr und solange dasfd
noch geöffnet ist, können Sie einen anderen Befehl aufrufen, der das ausführttruncate
.Zum Beispiel:
Was hier wichtig ist, ist, dass
truncate
obenhead
nur der Cursor für fd 1 innerhalb der Datei direkt nach der ersten Zeile bewegt wird. Es schreibt die erste Zeile neu, für die wir sie nicht brauchten, aber das ist nicht schädlich.Mit einem POSIX-Kopf könnten wir tatsächlich davonkommen, ohne die erste Zeile neu zu schreiben:
Hier verwenden wir die Tatsache, dass
head
die Cursorposition in ihrem Standard verschoben wird. Währendhead
POSIX normalerweise seine Eingaben von großen Blöcken lesen würde, um die Leistung zu verbessern, würde es (wo möglich) erfordern, dass esseek
kurz nach der ersten Zeile zurückgesetzt wird, wenn es darüber hinausgegangen wäre. Beachten Sie jedoch, dass dies nicht bei allen Implementierungen der Fall ist.Alternativ können Sie
read
in diesem Fall stattdessen den Befehl der Shell verwenden :quelle
STDIN
ähnlich wieperl
oben beschrieben abgeschnitten werden kanndd
kann jedoch bei jedem beliebigen absoluten Versatz in der Datei abgeschnitten werden . So können Sie den Byte-Offset der zweiten Zeile bestimmen und von dort mitdd bs=1 seek="$offset" of=file
Die Lösung des echten Mannes ist
oder als Einzeiler
Oder mit GNU sed:
(Nein, ich mache Witze. Ich würde einen interaktiven Editor verwenden.
vi .hgignore
GddZZ
)quelle
:wq
Over einen Vorteil hatZZ
.:x
das ist, was meine Finger automatisch tunZQ
ist das gleiche wie:q!
Sie können Vim im Ex-Modus verwenden:
2,
Wählen Sie die Zeilen 2 bis zum Ended
löschenx
speichern und schließenquelle
Für die direkte Dateibearbeitung können Sie auch den von Jürgen Hötzel in der Umleitungsausgabe von sed 's / c / d /' myFile nach myFile gezeigten Trick zum Öffnen von Dateien verwenden .
quelle
rm .hgignore
Ihr Strom ausfällt, nehmen Sie Stunden harter Arbeit weg. Ok, das spielt keine Rolle.hgignore
, aber warum sollten Sie etwas so Kompliziertes tun? Also meine Ablehnung: technisch korrekt, aber eine sehr schlechte Idee.perl -i
genau das macht zum Beispiel (für die Inplace-Bearbeitung), und ich wäre nicht überrascht, wenn einige Implementierungen dies auchsed -i
tun würden (obwohl die neueste Version von GNU diessed
nicht zu tun scheint).