Sie haben bereits einige sehr gute Antworten erhalten. Lassen Sie mich jedoch betonen, dass es sich um zwei verschiedene Konzepte handelt, deren Verständnis enorm hilft:
Hintergrund: Dateideskriptor vs. Dateitabelle
Ihr Dateideskriptor ist nur eine Zahl 0 ... n, der Index in der Dateideskriptortabelle in Ihrem Prozess. Gemäß Konvention ist STDIN = 0, STDOUT = 1, STDERR = 2 (beachten Sie, dass die Begriffe STDIN
usw. hier nur Symbole / Makros sind , die gemäß Konvention in einigen Programmiersprachen und Manpages verwendet werden; es gibt kein aktuelles "Objekt" mit dem Namen STDIN; z der Zweck dieser Diskussion, STDIN ist 0, etc.).
Diese Dateideskriptortabelle selbst enthält keinerlei Informationen darüber, was die eigentliche Datei ist. Stattdessen enthält es einen Zeiger auf eine andere Dateitabelle. Letzteres enthält Informationen zu einer tatsächlichen physischen Datei (oder einem Blockgerät oder einer Pipe oder was auch immer Linux über den Dateimechanismus adressieren kann) und weitere Informationen (z. B. ob es zum Lesen oder Schreiben dient).
Wenn Sie also >
oder <
in Ihrer Shell verwenden, ersetzen Sie einfach den Zeiger des jeweiligen Dateideskriptors, um auf etwas anderes zu verweisen. Die Syntax zeigt 2>&1
einfach Deskriptor 2 auf 1 Punkt. > file.txt
öffnet sich einfach file.txt
zum Schreiben und lässt STDOUT (Datei-Decsriptor 1) darauf verweisen.
Es gibt andere Extras, z. B 2>(xxx)
.: Erstellen eines neuen Prozesses xxx
, Erstellen einer Pipe, Verbinden des Dateideskriptors 0 des neuen Prozesses mit dem Leseende der Pipe und Verbinden des Dateideskriptors 2 des ursprünglichen Prozesses mit dem Schreibende der Pipe Rohr).
Dies ist auch die Grundlage für "Datei-Handle-Magie" in einer anderen Software als Ihrer Shell. Beispielsweise können Sie in Ihrem Perl-Skript dup
den STDOUT-Dateideskriptor in einen anderen (temporären) Deskriptor zerlegen und dann STDOUT erneut in einer neu erstellten temporären Datei öffnen. Ab diesem Zeitpunkt werden alle STDOUT-Ausgaben Ihres eigenen Perl-Skripts und alle system()
Aufrufe dieses Skripts in dieser temporären Datei gespeichert. Wenn Sie fertig sind, können Sie dup
Ihr STDOUT auf den temporären Deskriptor zurücksetzen, in dem Sie es gespeichert haben, und außerdem ist alles wie zuvor. Sie können in der Zwischenzeit sogar in diesen temporären Deskriptor schreiben. Während also Ihre aktuelle STDOUT-Ausgabe in die temporäre Datei verschoben wird, können Sie tatsächlich noch Daten in das echte STDOUT ausgeben (normalerweise der Benutzer).
Antworten
So wenden Sie die oben angegebenen Hintergrundinformationen auf Ihre Frage an:
In welcher Reihenfolge führt die Shell Befehle und Stream-Umleitungen aus?
Links nach rechts.
<command> > file.txt 2>&1
fork
aus einem neuen Prozess.
- Öffnen
file.txt
und speichern Sie den Zeiger im Dateideskriptor 1 (STDOUT).
- Zeigen Sie mit STDERR (Dateideskriptor 2) auf das, worauf der FD 1 gerade zeigt (was
file.txt
natürlich wieder der bereits geöffnete ist ).
exec
das <command>
Hierdurch wird stderr anscheinend zuerst an stdout umgeleitet, und dann wird die resultierende stdout an file.txt umgeleitet.
Dies wäre sinnvoll, wenn es nur eine Tabelle gäbe , aber wie oben erläutert, gibt es zwei. Dateideskriptoren verweisen nicht rekursiv aufeinander. Es macht keinen Sinn, "STDERR zu STDOUT umleiten". Der richtige Gedanke ist "point STDERR to wherever STDOUT points". Wenn Sie STDOUT später ändern, bleibt STDERR dort, wo es ist. Weitere Änderungen an STDOUT werden nicht auf magische Weise übernommen.