Vous avez déjà quelques très bonnes réponses. Permettez-moi de souligner cependant que deux concepts différents sont en jeu, dont la compréhension aide énormément:
Arrière-plan: descripteur de fichier et table de fichiers
Votre descripteur de fichier est juste un nombre 0 ... n, qui correspond à l'index de la table des descripteurs de fichier de votre processus. Par convention, STDIN = 0, STDOUT = 1, STDERR = 2 (notez que les termes, STDIN
etc. ne sont ici que des symboles / macros utilisés par convention dans certains langages de programmation et pages de manuel, il n’existe pas d’objet "réel" appelé STDIN; le but de cette discussion, STDIN est 0, etc.).
Cette table de descripteur de fichier en elle-même ne contient aucune information sur la nature du fichier. Au lieu de cela, il contient un pointeur sur une autre table de fichiers; ce dernier contient des informations sur un fichier physique réel (ou un périphérique bloc, ou un canal, ou tout autre élément que Linux peut adresser via le mécanisme de fichier), ainsi que des informations supplémentaires (que ce soit en lecture ou en écriture).
Ainsi, lorsque vous utilisez >
ou <
dans votre shell, vous remplacez simplement le pointeur du descripteur de fichier respectif pour désigner autre chose. La syntaxe 2>&1
pointe simplement le descripteur 2 sur 1 où. > file.txt
s'ouvre simplement file.txt
pour l'écriture et laisse STDOUT (le descripteur de fichier 1) pointer vers cela.
Il y a d'autres goodies, par exemple 2>(xxx)
(ex: créer un nouveau processus en cours d'exécutionxxx
, créer un canal, connecter le descripteur de fichier 0 du nouveau processus à la fin de la lecture du canal et connecter le descripteur de fichier 2 du processus original à la fin de l’écriture du processus). tuyau).
C’est aussi la base de la "magie du traitement de fichier" dans un logiciel autre que votre shell. Par exemple, vous pouvez, dans votre script Perl, dup
convertir le descripteur de fichier STDOUT en un autre (temporaire), puis rouvrir STDOUT dans un fichier temporaire nouvellement créé. À partir de ce moment, toutes les sorties STDOUT de votre propre script Perl et tous les system()
appels de ce script se retrouveront dans ce fichier temporaire. Lorsque vous avez terminé, vous pouvez rétablir dup
votre STDOUT dans le descripteur temporaire dans lequel vous l'avez enregistré, et tout est comme avant. Vous pouvez même écrire dans ce descripteur temporaire entre-temps. Ainsi, si votre sortie réelle de STDOUT est dirigée vers le fichier temporaire, vous pouvez toujours générer des éléments dans le vrai STDOUT (généralement, l'utilisateur).
Répondre
Pour appliquer les informations de base données ci-dessus à votre question:
Dans quel ordre le shell exécute-t-il les commandes et la redirection du flux?
De gauche à droite.
<command> > file.txt 2>&1
fork
hors d'un nouveau processus.
- Ouvrez
file.txt
et stockez son pointeur dans le descripteur de fichier 1 (STDOUT).
- Pointez STDERR (descripteur de fichier 2) sur quelque chose que le fd 1 pointe maintenant (qui est encore le déjà ouvert
file.txt
).
exec
le <command>
Apparemment, cela redirige stderr vers stdout en premier, puis la stdout résultante est redirigée vers file.txt.
Cela aurait du sens s'il n'y avait qu'une seule table, mais comme expliqué ci-dessus, il y en a deux. Les descripteurs de fichier ne se pointent pas de manière récursive, cela n'a aucun sens de penser "rediriger STDERR vers STDOUT". La bonne idée est de "pointer STDERR vers tous les points de STDOUT". Si vous changez de STDOUT plus tard, STDERR reste en place, il ne va pas comme par magie avec les modifications ultérieures apportées à STDOUT.