Pourquoi le comportement de `command 1> file.txt 2> file.txt` est-il différent de` command 1> file.txt 2> & 1`?

20

Lorsque vous souhaitez rediriger à la fois stdout et stderr vers le même fichier, vous pouvez le faire en utilisant command 1>file.txt 2>&1, ou command &>file.txt. Mais pourquoi le comportement est-il command 1>file.txt 2>file.txtdifférent des deux commandes ci-dessus?

Ce qui suit est une commande de vérification.

$ cat redirect.sh
#!/bin/bash

{ echo -e "output\noutput" && echo -e "error" 1>&2; } 1>file.txt 2>&1
{ echo -e "output\noutput" && echo -e "error" 1>&2; } 1>file1.txt 2>file1.txt
{ echo -e "error" 1>&2 && echo -e "output\noutput"; } 1>file2.txt 2>file2.txt
{ echo -e "output" && echo -e "error\nerror" 1>&2; } 1>file3.txt 2>file3.txt
{ echo -e "error\nerror" 1>&2 && echo -e "output"; } 1>file4.txt 2>file4.txt

$ ./redirect.sh

$ echo "---file.txt---"; cat file.txt;\
echo "---file1.txt---"; cat file1.txt; \
echo "---file2.txt---"; cat file2.txt; \
echo "---file3.txt---"; cat file3.txt; \
echo "---file4.txt----"; cat file4.txt;
 ---file.txt---
output
output
error
---file1.txt---
error

output
---file2.txt---
output
output
---file3.txt---
error
error
---file4.txt----
output
rror

En ce qui concerne les résultats, il semble que la deuxième chaîne d'écho écrase la première chaîne d'écho lorsque vous exécutez command 1>file.txt 2>file.txt, mais je ne sais pas pourquoi. (Y a-t-il une référence quelque part?)

fhiyo
la source

Réponses:

43

Vous devez savoir deux choses:

  • Un descripteur de fichier ouvert connu du côté mode d'application d'un processus fait référence à un objet du noyau interne appelé description de fichier , qui est une instance d'un fichier ouvert. Il peut y avoir plusieurs descriptions de fichiers par fichier et plusieurs descripteurs de fichiers partageant une description de fichier.
  • La position actuelle du fichier est un attribut d'une description de fichier . Ainsi, si plusieurs descripteurs de fichiers sont mappés sur une seule description de fichier, ils partagent tous la même position de fichier actuelle et une modification de la position de fichier adoptée à l'aide d'un tel descripteur de fichier affecte tous les autres descripteurs de fichier.

    Ces modifications sont adoptées par des processus appelant les read()/ readv(), write()/ writev(), lseek()et les appels système suchlike. La echocommande appelle write()/ writev()bien sûr.

Donc, ce qui se passe est le suivant:

  • command 1>file.txt 2>&1ne crée qu'une seule description de fichier, car le shell ouvre un fichier une seule fois. Le shell fait mapper à la fois la sortie standard et les descripteurs de fichier d'erreur standard sur cette description de fichier unique. Il duplique la sortie standard sur l'erreur standard. Ainsi, une écriture via l'un ou l'autre descripteur de fichier déplacera la position actuelle du fichier partagé: chaque écriture va après l'écriture précédente la description de fichier commune. Et comme vous pouvez le voir, les résultats des echocommandes ne se remplacent pas.
  • command 1>file.txt 2>file.txtcrée deux descriptions de fichier, car le shell ouvre deux fois le même fichier, en réponse aux deux redirections explicites. Les descripteurs de sortie standard et de fichier d'erreur standard correspondent à deux descriptions de fichier différentes, qui à leur tour sont mappées au même fichier unique. Les deux descriptions de fichier ont des positions de fichier actuelles entièrement indépendantes, et chaque écriture passe immédiatement à l'écriture précédente sur la même description de fichier. Et comme vous pouvez le voir, le résultat est que ce qui est écrit via l'un peut écraser ce qui est écrit via l'autre, de différentes manières selon l'ordre dans lequel vous exécutez les écritures.

Lectures complémentaires

JdeBP
la source
1
Devrait être une description de fichier ouvert plutôt qu'une description de fichier . Il s'agit davantage de l'enregistrement de la façon dont le fichier a été ouvert que du fichier lui-même. C'est la terminologie utilisée par la documentation POSIX, Linux, Solaris et GNU au moins.
Stéphane Chazelas
16

L'utilisation >indique de remplacer le fichier. Puisque stdout et stderr écrivent dans le fichier en deux opérations différentes, la dernière à écrire écrasera la première.

Tu peux faire:

command 1>>file.txt 2>>file.txt

ou

command &>file.txt Bash v4 et supérieur uniquement.

>> lui dit d'ajouter le fichier afin qu'il ne remplace pas la sortie des opérations précédentes.

&> est juste un moyen plus simple d'écrire 2>&1

Jesse_b
la source
2
pourquoi ls 1>&0et ls 0>&0affiche- t- il toujours la sortie de ls?
Yvain
Je suis surpris d'utiliser des >>œuvres. Pourquoi cela n'a-t-il pas le problème de deux descriptions de fichiers avec des décalages indépendants? @JdeBP, savez-vous? Je pensais que l'ouverture d'un fichier en mode ajout équivalait à l'ouverture en mode écriture, la recherche de la position finale, puis l'interdiction de poursuivre la recherche.
JoL
4
@jlmg: les fichiers en mode ajout peuvent être recherchés, mais chaque écriture est précédée d'une recherche implicite à la fin. Que cette recherche implicite soit atomique est moins clair pour moi.
Kevin
1
Cela dépend entièrement de la question suivante. Ceux comme celui-ci qui sont liés indiquent que la réponse est incomplète. Et il est peu probable que les gens recherchent les questions complémentaires sans vouloir également connaître la réponse complète. Par conséquent, il est très probable que de telles questions de suivi auront des réponses qui dupliquent les informations et seront donc fermées en tant que doublons.
trlkly
1
@Kevin, sur les systèmes de fichiers entièrement compatibles POSIX, la recherche implicite O_APPEND est atomique. Cela dit, tous les systèmes de fichiers n'implémentent pas correctement la sémantique appropriée - par exemple, NFS (au moins v3 et antérieur - je ne suis pas clair sur v4) n'a pas le support approprié intégré dans le protocole de fil, donc le serveur a aucun moyen de savoir si un client a ouvert un fichier avec O_APPEND.
Charles Duffy