Vous avez déjà reçu quelques très bonnes réponses. Permettez-moi de souligner qu'il y a deux concepts différents impliqués ici, dont la compréhension aide énormément:
Arrière-plan: descripteur de fichier vs. table de fichiers
Votre descripteur de fichier est juste un numéro 0 ... n, qui est l’index de la table de descripteur de fichier de votre processus. Par convention, STDIN = 0, STDOUT = 1, STDERR = 2 (notez que les termes STDIN
etc. ne sont que des symboles / macros utilisés conventionnellement dans certains langages de programmation et pages de manuel, il n'y a pas de véritable "objet" appelé STDIN; aux fins de cette discussion, STDIN est 0, etc.).
Cette table de descripteur de fichier en elle-même ne contient aucune information sur le contenu du fichier. Au lieu de cela, il contient un pointeur sur une autre table de fichiers; cette dernière contient des informations sur un fichier physique réel (ou périphérique de bloc, ou tube, ou tout ce que Linux peut adresser via le mécanisme de fichier) et plus d’informations (c.-à-d. si elle est destinée à la lecture ou à l’écriture).
Ainsi, lorsque vous utilisez >
ou <
dans votre shell, vous remplacez simplement le pointeur du descripteur de fichier respectif pour pointer vers autre chose. La syntaxe 2>&1
pointe simplement le descripteur 2 vers 1 point. > file.txt
ouvre simplement file.txt
pour l'écriture et laisse STDOUT (descripteur de fichier 1) pointer sur cela.
Il existe d’autres goodies, par ex. 2>(xxx)
(c.-à-d. créer un nouveau processus exécutant xxx
, créer un canal, connecter le descripteur de fichier 0 du nouveau processus à l'extrémité de lecture du canal et connecter le descripteur de fichier 2 du processus d'origine à la fin de l'écriture pipe).
Ceci est aussi la base de "magic handle de fichier" dans un autre logiciel que votre shell. Par exemple, dans votre script Perl, vous pouvez, dup
décrivez le descripteur de fichier STDOUT dans un autre fichier (temporaire), puis rouvrez STDOUT dans un fichier temporaire nouvellement créé. À partir de là, toutes les sorties STDOUT de votre script Perl et tous les appels system()
de ce script se retrouveront dans ce fichier temporaire. Une fois terminé, vous pouvez re dup
votre STDOUT sur le descripteur temporaire sur lequel vous l'avez enregistré, et hop, tout est comme avant. Vous pouvez même écrire dans ce descripteur temporaire pendant que votre sortie STDOUT va dans le fichier temporaire, vous pouvez toujours en sortir dans le fichier real STDOUT (généralement l'utilisateur).
Réponse
Pour appliquer les informations de base ci-dessus à votre question:
Dans quel ordre le shell exécute-t-il les commandes et redirige-t-il les flux?
De gauche à droite.
<command> > file.txt 2>&1
-
fork
sur un nouveau processus.
- Ouvrez
file.txt
et stockez son pointeur dans le descripteur de fichier 1 (STDOUT).
- Pointez STDERR (descripteur de fichier 2) sur le point auquel le fd 1 pointe maintenant (ce qui est encore le
file.txt
ouvert).
-
exec
le <command>
Cela redirige apparemment stderr vers stdout en premier, puis la sortie stdout résultante est redirigée vers file.txt.
Cela aurait du sens s’il n’y avait que des tables une , mais comme expliqué ci-dessus, il y en a deux. Les descripteurs de fichiers ne se pointent pas récursivement, cela n'a aucun sens de penser "rediriger STDERR vers STDOUT". La pensée correcte est "point STDERR vers les points STDOUT". Si vous changez STDOUT plus tard, STDERR reste là où il est, cela ne va pas de pair avec d'autres modifications de STDOUT.