Ist die Umleitung mit `>>` äquivalent zu `>`, wenn die Zieldatei noch nicht existiert?

6836
Kamil Maciorowski

Betrachten Sie eine Muschel wie Bash oder sh. Der grundlegende Unterschied zwischen >und >>manifestiert sich in einem Fall, in dem die Zieldatei existiert:

  • > schneidet die Datei auf null und schreibt dann;
  • >> wird nicht abgeschnitten, sondern an das Ende der Datei angehängt.

Wenn die Datei nicht existiert, wird sie mit der Größe Null erstellt. dann geschrieben an. Dies gilt für beide Betreiber. Es kann den Anschein haben, dass die Operatoren gleichwertig sind, wenn die Zieldatei noch nicht vorhanden ist.

Sind sie wirklich

79

1 Antwort auf die Frage

107
Kamil Maciorowski

tl; dr

No. >>ist im Wesentlichen "immer bis zum Ende der Datei suchen", während >ein Zeiger auf den zuletzt geschriebenen Ort bleibt .


Volle Antwort

(Hinweis: Alle meine Tests wurden unter Debian GNU / Linux 9 durchgeführt).

Ein weiterer Unterschied

Nein, sie sind nicht gleichwertig. Es gibt noch einen anderen Unterschied. Es kann sich selbst manifestieren, unabhängig davon, ob die Zieldatei zuvor existiert hat oder nicht.

Um dies zu beobachten, führen Sie einen Prozess aus, der Daten generiert und mit >oder >>(z pv -L 10k /dev/urandom > blob. B. ) zu einer Datei umleitet . Lassen Sie es laufen und ändern Sie die Größe der Datei (zB mit truncate). Sie werden sehen, dass >der (wachsende) Offset >>beibehalten wird, während er immer an das Ende angehängt wird.

  • Wenn Sie die Datei auf eine kleinere Größe kürzen (kann Null sein)
    • >Es ist mir egal, es schreibt am gewünschten Offset, als wäre nichts passiert; Unmittelbar nach dem Abschneiden des Versatzes über das Ende der Datei hinaus wird die Datei ihre alte Größe wiedererlangen und weiter wachsen. Die fehlenden Daten werden mit Nullen aufgefüllt (wenn möglich sparsam).
    • >> Wird an das neue Ende angehängt, wächst die Datei von ihrer abgeschnittenen Größe.
  • Wenn Sie die Datei vergrößern
    • >Es ist mir egal, es schreibt am gewünschten Offset, als wäre nichts passiert; Unmittelbar nach dem Ändern der Größe befindet sich der Versatz irgendwo in der Datei. Dies führt dazu, dass die Datei für eine Weile nicht mehr wächst, bis der Versatz das neue Ende erreicht. Dann wird die Datei normal groß.
    • >> wird an das neue Ende angehängt, die Datei wird größer und größer.

Ein anderes Beispiel ist das Hinzufügen (mit einem separaten >>) etwas Extra, wenn der Datenerzeugungsprozess ausgeführt wird und in die Datei geschrieben wird. Dies ähnelt dem Vergrößern der Datei.

  • Der Erzeugungsprozess mit >wird an seinem gewünschten Offset schreiben und die zusätzlichen Daten eventuell überschreiben.
  • Der Generierungsprozess mit >>überspringt die neuen Daten und hängt an diesen an (die Race-Bedingung kann auftreten, die beiden Streams können verschachtelt sein, dennoch sollten keine Daten überschrieben werden).

Beispiel

Ist es in der Praxis wichtig? Es gibt diese Frage :

Ich führe einen Prozess aus, der auf stdout viel ausgibt. Alles in eine Datei senden [...] Kann ich ein Protokoll-Rotationsprogramm verwenden?

Diese Antwort sagt die Lösung logrotatemit copytruncateOption, die wie folgt wirkt:

Kürzen Sie die ursprüngliche Protokolldatei an Ort und Stelle, nachdem Sie eine Kopie erstellt haben, anstatt die alte Protokolldatei zu verschieben und optional eine neue zu erstellen.

Nach dem, was ich oben geschrieben habe, >wird das abgeschnittene Protokoll durch das Umleiten mit innerhalb kürzester Zeit groß. Sparsamkeit spart den Tag, es sollte kein nennenswerter Speicherplatz verschwendet werden. Dennoch enthält jedes aufeinanderfolgende Protokoll mehr und mehr führende Nullen, die völlig unnötig sind.

Wenn jedoch logrotateKopien erstellt werden, ohne die Spärlichkeit zu erhalten, benötigen diese führenden Nullen bei jeder Kopie mehr und mehr Speicherplatz. Ich habe das Werkzeugverhalten nicht untersucht. Es kann klug genug sein, wenn die Komprimierung im Verlauf der Zeit geringer ist (wenn die Komprimierung aktiviert ist). Dennoch können die Nullen nur Probleme verursachen oder bestenfalls neutral sein; nichts Gutes in ihnen.

In diesem Fall ist die Verwendung >>statt statt >wesentlich besser, auch wenn die Zieldatei gerade erstellt wird.


Performance

Wie wir sehen, verhalten sich die beiden Operatoren nicht nur zu Beginn, sondern auch später. Dies kann zu geringfügigen (geringfügigen) Leistungsunterschieden führen. Zur Zeit habe ich keine aussagekräftigen Testergebnisse, die es unterstützen oder widerlegen könnten, aber ich denke, Sie sollten nicht automatisch davon ausgehen, dass ihre Leistung im Allgemeinen die gleiche ist.

Also ist ">>" im Wesentlichen "immer am Ende der Datei suchen", während ">" einen Zeiger auf den zuletzt geschriebenen Ort verwaltet. Es scheint, dass es einen geringfügigen Leistungsunterschied in ihrer Funktionsweise gibt ... Mokubai vor 6 Jahren 9
@Mokubai Gut gesagt. Ich werde Ihren ersten Satz als "dr" verwenden, wenn Sie nichts dagegen haben. Kamil Maciorowski vor 6 Jahren 0
Keine Sorge, mach es. ;) Mokubai vor 6 Jahren 0
Wo finden wir einen dokumentierten Hinweis darauf, wie sich `>` und `>>` unterscheiden? jjmontes vor 6 Jahren 0
Auf der Ebene des Systemaufrufs verwendet `>>` das Flag [`O_APPEND` für` open () `] (http://man7.org/linux/man-pages/man2/open.2.html). Tatsächlich verwendet `>` `O_TRUNC`, während` >> `dies nicht tut. Die Kombination von O_TRUNC | O_APPEND` wäre auch möglich, die Shellsprache bietet diese Funktion einfach nicht. ilkkachu vor 6 Jahren 10
@jjmontes, die Standardquelle wäre POSIX: http://pubs.opengroup.org/onlinepubs/9699919799.2018edition/utilities/V3_chap02.html#tag_18_07. Natürlich enthält das Handbuch von Bash auch Beschreibungen zu den Umleitungsoperatoren, einschließlich der Nicht-Standardoperatoren diejenigen, die es unterstützt: https://www.gnu.org/software/bash/manual/html_node/Redirections.html ilkkachu vor 6 Jahren 3
@ilkkachu Ich fand das interessant, da es Details zu O_APPEND erklärt, über die ich mich nach Ihrem Kommentar wunderte :): https://stackoverflow.com/questions/1154446/is-file-append-atomic-in-unix jjmontes vor 6 Jahren 2
@KamilMaciorowski Der Grund, warum ich vermute, dass es einen kleinen Leistungsunterschied geben kann, ist, dass "AlwaysSoOE to EOF" impliziert, dass entweder "checkLengthOfFile ()" gefolgt von "writeAtLocation (endOfFile)" vorhanden ist, während ">" einfach eine writeAtLocation ausführt (currentLocation) `. Beide würden die gleichen Überprüfungen auf Dateisystemebene auslösen, um sicherzustellen, dass die Datei lang genug ist oder eine spärliche Erweiterung benötigt, aber es ist ein zusätzlicher Schritt erforderlich, um die Länge zuerst zu überprüfen. Ich würde mit gutem Festplatten-Caching erwarten, dass der Unterschied zwischen trivial und nicht existent liegen würde ... Mokubai vor 6 Jahren 0
@Mokubai, Jedes vernünftige Betriebssystem hätte die Dateilänge zur Hand, wenn es geöffnet ist, und das Prüfen einer Flagge und das Verschieben des Versatzes bis zum Ende sollten in der anderen Buchhaltung verschwinden. Der Versuch, O_APPEND mit einem "lseek ()" vor jedem "write ()" zu emulieren, wäre anders, es wäre jedoch der zusätzliche Systemaufruf-Overhead. (Und natürlich würde es nicht funktionieren, da ein anderer Prozess dazwischen schreiben könnte.) ilkkachu vor 6 Jahren 1
@ilkkachu ich bekomme dich. Wenn Sie Ihren anderen Kommentar lesen, wird dies alles innerhalb der Bits und Bobs auf Dateisystemebene behandelt und ist im Wesentlichen kostenlos, da das Dateihandle geöffnet wird. Ich gebe zu, dass mein Kommentar aus einer etwas naiven Position stammt und veraltetes Wissen darüber enthält, wie Dinge * früher * zu erledigen waren ... Mokubai vor 6 Jahren 1
@Mokubai Virtuelle Unix / Linux-Dateisysteme implementieren normalerweise eine vnode-Struktur für jede geöffnete Datei. BSDs sind unter [https://man.openbsd.org/vnode.9] (https://man.openbsd.org/vnode.9) zu sehen. Ich habe BSD nicht genau untersucht, vermute aber die void * v_data; Das Feld / * private Daten für fs * / `in der Struktur enthält die relevanten Metadaten, wie z. B. die aktuelle Dateilänge und den aktuellen Datei-Offset. Da "write ()" - Operationen das Sperren der Strukturen beinhalten, ist das Herausfinden der zu schreibenden Stellen ein einfaches "Nachschlagen der Dateiöffnungsflags, dann Verwenden der aktuellen Länge oder des Versatzes abhängig vom Wert der Flaggen". Andrew Henle vor 6 Jahren 0
Natürlich ist [logrotate nicht die Antwort dieses Jahrhunderts] (http://jdebp.info./FGA/do-not-use-logrotate.html). https://superuser.com/a/868519/38062 https://superuser.com/a/291397/38062 https://superuser.com/a/291397/38062 https://unix.stackexchange.com/a / 392924/5132 JdeBP vor 6 Jahren 1
@AndrewHenle: Ich glaube, dass Sie das (zumindest teilweise) rückwärts betrachten. Einerseits bin ich überrascht, dass die Dateigröße nicht eindeutig in der vnode-Struktur enthalten ist. Es scheint, dass es ein Attribut ist, das allen Dateisystemtypen gemeinsam ist. Aber vielleicht ist es unter "v_data" begraben. Denken Sie jedoch daran, dass bei mehreren Prozessen dieselbe Datei gleichzeitig geöffnet sein kann, * mit unterschiedlichen Datei-Offsets. * Der Datei-Offset kann nicht in der Struktur `inode` /` vnode` liegen, sondern muss sich in der Datei befinden (Beschreibung) Struktur; Es können mehrere `file`-Tabelleneinträge für eine einzelne Datei vorhanden sein. … (Fortsetzung) Scott vor 6 Jahren 0
(Fortsetzung)… Ich konnte nur eine sehr alte Kopie von `file.h` finden [hier] (https://pages.lip6.fr/Pierre.Sens/srcv6/file.h.html). [Diese Vorlesung] (http://www.cs.rpi.edu/academics/courses/fall04/os/c18/) zeigt die Beziehung zwischen den Strukturen. [Dieser Code] (https://minnie.tuhs.org//cgi-bin/utree.pl?file=4BSD/usr/src/sys/sys/sys2.c) von BSD 4 zeigt, dass `f_offset` von kopiert wird die `file`-Struktur für` u.u_offset` und [this] (https://minnie.tuhs.org//cgi-bin/utree.pl?file=4BSD/usr/src/sys/sys/rdwri. c) zeigt `` readi`` und `` writei`` mit `u.u_offset` als Offset in die Datei. … (Fortsetzung) Scott vor 6 Jahren 0
(Fortsetzung)… PS Ich konnte nicht finden, wo 'O_APPEND' gehandhabt wird. Scott vor 6 Jahren 0