Es ist bekannt, dass ein Befehl wie dieser:
cat filename | some_sed_command >filename
Löscht den Dateinamen der Datei, da die vor dem Befehl ausgeführte Umleitung der Ausgabe dazu führt, dass der Dateiname abgeschnitten wird.
Man könnte das Problem folgendermaßen lösen:
cat file | some_sed_command | tee file >/dev/null
Ich bin mir aber nicht sicher, ob dies auf jeden Fall funktionieren würde: Was passiert, wenn die Datei (und das Ergebnis des Befehls sed) sehr groß ist? Wie kann das Betriebssystem vermeiden, Inhalte zu überschreiben, die noch nicht gelesen werden? Ich sehe, dass es auch einen Schwammbefehl gibt, der auf jeden Fall funktionieren sollte: Ist er "sicherer" als Tee?
command-line
bash
tee
VeryHardCoder
quelle
quelle
Antworten:
Nein .
Die Chancen
file
werden abgeschnitten, aber es gibt keine Garantie,cat file | some_sed_command | tee file >/dev/null
dass sie nicht abgeschnitten werdenfile
.Es hängt alles davon ab, welcher Befehl zuerst verarbeitet wird, im Gegensatz zu dem, was man erwarten kann, werden Befehle in einer Pipe nicht von links nach rechts verarbeitet . Es gibt keine Garantie dafür, welcher Befehl zuerst ausgewählt wird. Man kann ihn sich also genauso gut als zufällig ausgewählt vorstellen und sich niemals darauf verlassen, dass die Shell den beleidigenden Befehl nicht auswählt.
Da die Wahrscheinlichkeit, dass der fehlerhafte Befehl zuerst zwischen drei Befehlen ausgewählt wird, geringer ist als die Wahrscheinlichkeit, dass der fehlerhafte Befehl zuerst zwischen zwei Befehlen ausgewählt wird, ist es weniger wahrscheinlich,
file
dass er abgeschnitten wird, aber es wird trotzdem passieren .script.sh
::Verwenden Sie also niemals so etwas
cat file | some_sed_command | tee file >/dev/null
. Verwenden Siesponge
wie von Oli vorgeschlagen.Alternativ kann man für dichtere Umgebungen und / oder relativ kleine Dateien eine Here-Zeichenfolge und eine Befehlsersetzung verwenden, um die Datei zu lesen, bevor ein Befehl ausgeführt wird:
quelle
Für
sed
speziell, können Sie seine Verwendung-i
an Ort und Stelle Argumente. Es wird nur in der geöffneten Datei gespeichert, z.Wenn Sie etwas Stärkeres tun möchten, vorausgesetzt, Sie tun mehr als
sed
, ja, können Sie das Ganze mitsponge
(aus demmoreutils
Paket) puffern, wodurch der gesamte Standard "aufgesaugt" wird, bevor Sie in die Datei schreiben. Es ist wie,tee
aber mit weniger Funktionalität. Für die grundlegende Verwendung ist es jedoch so ziemlich ein Drop-In-Ersatz:Ist das sicherer? Bestimmt. Es hat wahrscheinlich Grenzen durch. Wenn Sie also etwas Kolossales tun (und nicht direkt mit sed bearbeiten können), möchten Sie möglicherweise Ihre Änderungen an einer zweiten Datei vornehmen und
mv
diese Datei dann wieder auf den ursprünglichen Dateinamen zurücksetzen. Das sollte atomar sein (damit alles, was von diesen Dateien abhängt, nicht kaputt geht, wenn sie ständigen Zugriff benötigen).quelle
Sie können Vim im Ex-Modus verwenden:
%
Wählen Sie alle Zeilen aus!
Führen Sie den Befehl ausx
Speichern und Beendenquelle
Oh, aber
sponge
ist nicht die einzige Option; Sie müssen nicht erhaltenmoreutils
, damit dies richtig funktioniert. Jeder Mechanismus funktioniert, solange er die folgenden zwei Anforderungen erfüllt:Sie sehen, das bekannte Problem, auf das sich das OP bezieht, ist, dass die Shell alle Dateien erstellt, die für die Arbeit der Pipes erforderlich sind, bevor überhaupt mit der Ausführung der Befehle in der Pipeline begonnen wird. Es ist also die Shell, die tatsächlich abschneidet Die Ausgabedatei (die leider auch die Eingabedatei ist), bevor einer der Befehle überhaupt ausgeführt werden konnte.
Der
tee
Befehl funktioniert nicht, obwohl er die erste Anforderung erfüllt, da er die zweite Anforderung nicht erfüllt: Er erstellt die Ausgabedatei immer sofort nach dem Start, sodass er im Wesentlichen so schlecht ist wie das Erstellen einer Pipe direkt in die Ausgabedatei. (Es ist tatsächlich schlimmer, weil seine Verwendung eine nicht deterministische zufällige Verzögerung einführt, bevor die Ausgabedatei abgeschnitten wird. Sie könnten also denken, dass es funktioniert, obwohl dies nicht der Fall ist.)Alles, was wir brauchen, um dieses Problem zu lösen, ist ein Befehl, der alle Eingaben puffert, bevor eine Ausgabe erzeugt wird, und der den Ausgabedateinamen als Parameter akzeptieren kann, damit wir seine Ausgabe nicht weiterleiten müssen die Ausgabedatei. Ein solcher Befehl ist
shuf
. Folgendes wird also dasselbe bewirkensponge
:Der
--random-source=/dev/zero
Teilshuf
versucht, seine Sache zu erledigen, ohne überhaupt zu mischen, sodass er Ihre Eingabe puffert, ohne sie zu ändern.quelle