Ist es sicher, stdout und stderr ohne Dateideskriptorkopien in dieselbe Datei umzuleiten?

27

Ich beginne in einem leeren Verzeichnis.

$ touch aFile
$ ls
aFile

Dann habe ich lszwei Argumente, von denen sich eines nicht in diesem Verzeichnis befindet. Ich leite beide Ausgabestreams in eine Datei namens um output. Ich benutze >>, um nicht gleichzeitig zu schreiben.

$ ls aFile not_exist >>output 2>>output
$ cat output
ls: cannot access 'not_exist': No such file or directory
aFile

Welches scheint zu funktionieren. Gibt es Gefahren für diesen Ansatz?

exit_status
quelle
6
Das war eine schnelle Abstimmung. Es dauerte ungefähr fünf Sekunden. Können Sie mir sagen, wie Sie die Würdigkeit meiner Frage so schnell beurteilen können? Und noch besser, was ist daran falsch, damit ich es verbessern kann?
exit_status
Warum verwenden Sie ls aFile not_exist &>>outputhier nicht den Standard ? (Beachten Sie, ich
gehe
5
Denn das hilft mir nicht zu verstehen, worüber ich frage. Ich weiß, wie ich diese Streams portabel sogar in dieselbe Datei umleiten kann. Ich möchte wissen, ob etwas mit dem, was ich in der Frage vorgeschlagen habe, nicht stimmt. @FedonKadifeli
exit_status
1
@FedonKadifeli &>>ist NICHT Standard. Es ist eine DEPRECATED, mehrdeutige Syntax, die in verschiedenen Shells unterschiedlich funktioniert. Ich frage mich, woher ihr eure Sachen bekommt.
Onkel Billy
4
Bash ist kein Standard . Die POSIX - Standard schreibt vor , dass ls &>>foo ...als zwei Comands analysiert werden soll ls &und >>foo ..., und dies ist die Art und Weise andere Shells wie der /bin/shvon Ubuntu es parsen. Wenn es veraltet ist, können Sie hier nachsehen - obwohl ich nicht vorgebe, dass das irgendeine Art von Autorität ist. Sie können die bashBetreuer jedoch fragen , ob sie dies für eine gute Idee halten.
Onkel Billy

Antworten:

22

Nein, es ist nicht nur so sicher wie der Standard >>bar 2>&1.

Wenn du schreibst

foo >>bar 2>>bar

Sie öffnen die barDatei zweimal mit O_APPENDund erstellen zwei völlig unabhängige Dateiobjekte [1] mit jeweils eigenem Status (Zeiger, Öffnungsmodus usw.).

Dies ist sehr viel anders , als wenn 2>&1nur der dup(2)Systemaufruf aufgerufen wird, und macht die Aliase stderr und stdout für dasselbe Dateiobjekt austauschbar.

Nun gibt es ein Problem damit:

O_APPENDkann zu beschädigten Dateien auf NFS-Dateisystemen führen, wenn mehrere Prozesse gleichzeitig Daten an eine Datei anhängen. Dies liegt daran, dass NFS das Anhängen an eine Datei nicht unterstützt, sodass der Client-Kernel diese simulieren muss, was ohne eine Race-Bedingung nicht möglich ist.

Sie können in der Regel auf der Wahrscheinlichkeit der Datei zählen wie barin foo >>bar 2>&1bis aus zwei getrennten Orten zur gleichen Zeit geschrieben wird ziemlich niedrig zu sein. Aber >>bar 2>>barSie haben es ohne Grund nur um ein Dutzend Größenordnungen erhöht.

[1] "Dateibeschreibungen öffnen" in POSIX-Jargon.

Mosvy
quelle
3
Formal ist es für Dateien im Append-Modus sicher . Das genannte Problem ist ein Fehler in NFS, der es als Dateisystem ungeeignet (nicht POSIX-konform) macht. Für den Non-Append-Mode-Fall ist dies jedoch niemals sicher.
R ..
1
Das ist immateriell. Das Double-Append des OP ist nicht sicher zu verwenden (zusätzlich dazu, dass es völlig sinnlos ist). Und O_APPENDist sowieso eine Art Pfusch - ziemlich mühsam, um es richtig zu implementieren.
Mosvy
Ich glaube, dass die NFS-Rennbedingung nur zwischen verschiedenen Kunden besteht. Das Client-Betriebssystem sollte alle Schreibvorgänge zwischen seinen Prozessen koordinieren.
Barmar
@Barmar das wäre wahr, wenn sich das Client-Betriebssystem nur um seine eigene Ansicht einer nfs-Datei kümmern würde. Beim Schreiben in eine NFS-Datei, die mit geöffnet wurde O_APPEND, ruft der Client zuerst die "echte" Größe der Datei vom Server ab ("validieren" Sie den Inode) und führen dann die Aktualisierung von Suche + Schreiben + zwischengespeichertem Inode durch, und nur der letzte Teil ist Dies bedeutet, dass der erste Teil immer noch eine veraltete Größe vom Server abrufen und die richtige Größe vom lokalen / zwischengespeicherten Inode überschreiben kann. Gleiches Problem mit lseek(SEEK_END).
Mosvy
Ich verstehe immer noch nicht, wie dies zu Racebedingungen zwischen zwei Streams auf demselben Client führen kann. Beide Streams sollten sich auf denselben lokalen zwischengespeicherten Inode beziehen.
Barmar
22

Was passiert, wenn du es tust?

some_command >>file 2>>file

Das filewird zum zweimaligen Anhängen geöffnet. Dies ist auf einem POSIX-Dateisystem sicher möglich. Alle Schreibvorgänge, die beim Öffnen der Datei zum Anhängen auftreten, werden am Ende der Datei ausgeführt, unabhängig davon, ob die Daten über den Standardausgabestream oder den Standardfehlerstream übertragen werden.

Dies setzt die Unterstützung von atomaren Schreiboperationen für Anhänge im zugrunde liegenden Dateisystem voraus. Einige Dateisysteme wie NFS unterstützen kein atomares Anhängen. Siehe z. B. die Frage "Ist der Dateianhang unter UNIX atomar?" Auf StackOverflow.

Verwenden

some_command >>file 2>&1

würde aber auch auf NFS funktionieren.

Verwenden Sie jedoch

some_command >file 2>file

ist nicht sicher, da die Shell die Ausgabedatei (zweimal) abschneidet und alle Schreibvorgänge auf einem der Streams die Daten überschreiben, die bereits vom anderen Stream geschrieben wurden.

Beispiel:

$ { echo hello; echo abc >&2; } >file 2>file
$ cat file
abc
o

Die helloZeichenfolge wird zuerst geschrieben (mit einem abschließenden Zeilenumbruch), und dann wird die Zeichenfolge, abcauf die ein Zeilenumbruch folgt, aus einem Standardfehler geschrieben, wobei der Wert überschrieben wird hell. Das Ergebnis ist der String abcmit einer neuen Zeile, gefolgt von dem Rest der ersten echoAusgabe, einer ound einer neuen Zeile.

Das Vertauschen der beiden echoumwickelten Produkte nur helloin der Ausgabedatei, da diese Zeichenfolge zuletzt geschrieben und länger als die abcZeichenfolge ist. Die Reihenfolge, in der die Weiterleitungen erfolgen, spielt keine Rolle.

Es wäre besser und sicherer, das Idiomatischere zu verwenden

some_command >file 2>&1
Kusalananda
quelle
1
Während dies für moderne Shells zutrifft, war dies bei der Bourne- oder Thomson-Shell (woher sie >>stammt) nicht der Fall , wo >>zum Schreiben geöffnet und bis zum Ende gesucht wurde (ich nehme an, weil O_APPEND damals noch nicht erfunden war). Auch unter Solaris 10 /bin/sh -c '(echo a; echo b >&2) >> file 2>> file; cat file'gibt b.
Stéphane Chazelas
@ StéphaneChazelas Ist dies ein Problem mit der Implementierung von Solaris 10 shoder mit seinem Dateisystem?
Kusalananda
1
Das war, was >>ursprünglich getan wurde, es wurde nicht mit O_APPEND geöffnet, es wurde ohne und bis zum Ende geöffnet. Es ist nicht so sehr ein Problem, es ist, was es tat und dokumentiert wurde, um es zu tun.
Stéphane Chazelas
0

Es kommt darauf an, was Sie erreichen wollen. Sie müssen entscheiden, ob Fehler in derselben Datei wie die Ausgabe auftreten dürfen. Dies ist nur das Speichern von Text in einer Datei mit der Funktionalität der Shell, mit der Sie nach Belieben umleiten können. Es gibt kein absolutes Ja oder Nein. Da alles unter Linux auf verschiedene Arten gemacht werden kann, ist dies meine Art. ls notExistingFile existingFile >> output 2>&1 Die Frage zu beantworten: In Bezug auf die Umleitung selbst ist es ja vollkommen sicher.

Engel
quelle
Es geht um mehr als das, was Sie hier sagen. Die gleiche Übung mit überschreibt >stattdessen >>einige Zeichen. Es ist also nicht nur so, dass die Shell mir erlaubt, umzuleiten, denn wenn ich mit umzuleiten >, ist das Ergebnis anders. Also gibt es Nuancen mit >, gibt es welche mit >>?
exit_status
Ja, es wird anders sein. Wie gesagt, es hängt von Ihrem Ziel ab >- Überschreiben. >>- anhängen
Engel