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 STDINusw. 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>&1einfach Deskriptor 2 auf 1 Punkt. > file.txtöffnet sich einfach file.txtzum 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 dupden 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 dupIhr 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.txtund speichern Sie den Zeiger im Dateideskriptor 1 (STDOUT).
- Zeigen Sie mit STDERR (Dateideskriptor 2) auf das, worauf der FD 1 gerade zeigt (was
file.txtnatü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.