Est-ce une faute de frappe dans la section de redirection du manuel Bash?

13
Note that the order of redirections is significant.  For example, the command

          ls > dirlist 2>&1

   directs both standard output and standard error to the file dirlist, 
   while the command

          ls 2>&1 > dirlist

   directs  only  the  standard  output  to  file  dirlist,  because the 
   standard error was duplicated from the standard output before the standard
   output was redirected to dirlist.

Maintenant, cette dernière partie me prête à confusion. Dans ce cas, toute erreur standard serait imprimée sur le terminal et tout STDOUT irait dans le fichier de liste. C'est ce qui se passerait, mais ce n'est pas ainsi que je comprends le manuel.

Il semble qu'il devrait dire "parce que l'erreur standard a été dupliquée à partir de la sortie standard APRÈS que la sortie standard a été redirigée vers dirlist". Si STDERR a été envoyé à STDOUT avant que STDOUT soit dirigé vers un fichier, le fichier ne contiendrait-il pas STDOUT ET STDERR?

Quelqu'un peut-il clarifier cela pour moi? Est-ce juste une mauvaise compréhension de la lecture de ma part? L'utilisation du mot duplication me semble un peu étrange dans ce contexte. Peut-être que cela me jette.

Gregg Leventhal
la source
1
Un cas classique de mélange d'opérations "par valeur" vs "par référence". Lorsque vous dupliquez un descripteur de fichier, il s'agit d'une opération par valeur . En programmation, après a = 1; b = a; a = 2vous vous attendez a == 2 && b == 1à être vrai. La redirection 2>&1est similaire à l' b = aaffectation - c'est par valeur, pas par référence. 2>&1ne relie pas le descripteur de fichier 2 au descripteur de fichier 1 pour toute l'éternité - il s'agit toujours de 2 descripteurs de fichiers distincts, qui pointent vers le même fichier.
jw013

Réponses:

23

La duplication est vraiment la partie importante ici.

Voyons où vont les descripteurs de fichiers avant la redirection. Il s'agit normalement du terminal actuel, par exemple:

STDOUT ---> /dev/pts/1
STDERR ---> /dev/pts/1

Maintenant, si nous appelons ls -lsans redirection, les messages de sortie et d'erreur vont à mon terminal sous /dev/pts/1.

Si nous redirigeons d'abord le STDOUTvers un fichier ( ls -l > dirlist), cela ressemble à ceci:

STDOUT ---> /home/bon/dirlist
STDERR ---> /dev/pts/1

Lorsque nous redirigeons ensuite vers STDERRun doublon du STDOUTdescripteur de fichier ( ls -l > dirlist 2>&1), STDERRva vers un doublon de /home/bon/dirlist:

STDOUT ---> /home/bon/dirlist
STDERR ---> /home/bon/dirlist

Si nous voulions d' abord rediriger STDERRvers un double du STDOUTdescripteur de fichier de ( ls -l 2>&1):

STDOUT ---> /dev/pts/1
STDERR ---> /dev/pts/1

et puis STDOUT dans un fichier ( ls -l 2>&1 > dirlist), nous obtiendrions ceci:

STDOUT ---> /home/bon/dirlist
STDERR ---> /dev/pts/1

Ici, STDERRva toujours au terminal.

Vous voyez, l'ordre dans la page de manuel est correct.


Test de la redirection

Maintenant, vous pouvez le tester vous-même. En utilisant ls -l /proc/$$/fd/, vous voyez où STDOUT(avec fd 1) et STDERR(avec fd 2), vont pour le processus actuel:

$ ls -l /proc/$$/fd/
total 0
lrwx------ 1 bon bon 64 Jul 24 18:19 0 -> /dev/pts/1
lrwx------ 1 bon bon 64 Jul 24 18:19 1 -> /dev/pts/1
lrwx------ 1 bon bon 64 Jul 24 07:41 2 -> /dev/pts/1
lrwx------ 1 bon bon 64 Jul 24 18:19 255 -> /dev/pts/1

Créons un petit script shell qui montre où vos descripteurs de fichiers sont pointés. De cette façon, nous obtenons toujours l'état lors de l'appel ls, y compris toute redirection à partir du shell appelant.

$ cat > lookfd.sh
#!/bin/sh
ls -l /proc/$$/fd/
^D
$ chmod +x lookfd.sh

(Avec CtrlD, vous envoyez une fin de fichier et arrêtez ainsi la catlecture de la commande STDIN.)

Maintenant, appelez ce script avec différentes combinaisons de redirection:

$ ./lookfd.sh 
total 0
lrwx------ 1 bon bon 64 Jul 24 19:08 0 -> /dev/pts/1
lrwx------ 1 bon bon 64 Jul 24 19:08 1 -> /dev/pts/1
lrwx------ 1 bon bon 64 Jul 24 19:08 2 -> /dev/pts/1
lr-x------ 1 bon bon 64 Jul 24 19:08 255 -> /home/bon/lookfd.sh
$ ./lookfd.sh > foo.out
$ cat foo.out 
total 0
lrwx------ 1 bon bon 64 Jul 24 19:10 0 -> /dev/pts/1
l-wx------ 1 bon bon 64 Jul 24 19:10 1 -> /home/bon/foo.out
lrwx------ 1 bon bon 64 Jul 24 19:10 2 -> /dev/pts/1
lr-x------ 1 bon bon 64 Jul 24 19:10 255 -> /home/bon/lookfd.sh
$ ./lookfd.sh 2>&1 > foo.out
$ cat foo.out 
total 0
lrwx------ 1 bon bon 64 Jul 24 19:10 0 -> /dev/pts/1
l-wx------ 1 bon bon 64 Jul 24 19:10 1 -> /home/bon/foo.out
lrwx------ 1 bon bon 64 Jul 24 19:10 2 -> /dev/pts/1
lr-x------ 1 bon bon 64 Jul 24 19:10 255 -> /home/bon/lookfd.sh
$ ./lookfd.sh > foo.out 2>&1
$ cat foo.out 
total 0
lrwx------ 1 bon bon 64 Jul 24 19:11 0 -> /dev/pts/1
l-wx------ 1 bon bon 64 Jul 24 19:11 1 -> /home/bon/foo.out
l-wx------ 1 bon bon 64 Jul 24 19:11 2 -> /home/bon/foo.out
lr-x------ 1 bon bon 64 Jul 24 19:11 255 -> /home/bon/lookfd.sh

Vous pouvez voir que les descripteurs de fichiers 1 (pour STDOUT) et 2 (pour STDERR) varient. Pour le plaisir, vous pouvez également rediriger STDINet voir le résultat:

$ ./lookfd.sh < /dev/zero
total 0
lr-x------ 1 bon bon 64 Jul 24 19:18 0 -> /dev/zero
lrwx------ 1 bon bon 64 Jul 24 19:18 1 -> /dev/pts/1
lrwx------ 1 bon bon 64 Jul 24 19:18 2 -> /dev/pts/1
lr-x------ 1 bon bon 64 Jul 24 19:18 255 -> /home/bon/lookfd.sh

(Question laissée au lecteur: où pointe le descripteur de fichier 255? ;-))

Dubu
la source
+1 - excellente réponse. Des exemples extrêmement bien écrits et formidables. Je vous remercie!!!
slm
Je vois, je pense que mon malentendu était que la redirection serait persistante pour toutes les commandes suivantes, de sorte que tout STDERR pour le reste de la ligne irait à STDOUT.
Gregg Leventhal
2

Non, le manuel est correct.

Si au début 1 pointe vers le terminal, et 2 également vers le terminal, alors:

command  2>&1   1>somewhere

L'évaluation de la redirection se fera de gauche à droite.

Il évalue 2>&1donc D'ABORD, et donc PREMIÈRE copie ce que fd pointait 1(c'est-à-dire le descripteur de fichier the terminal, généralement / dev / tty) dans fd 2.

Donc, à ce point, fd pointe 2maintenant vers où fd pointait 1( the terminal)

Et ALORS il évalue la 1>somewherepièce, et copiera donc le descripteur de fichier de somewheredans fd 1(donc à ce point, fd 1pointe maintenant vers somewhere, et fd 2pointe toujours vers the terminal)

Donc, il imprime en effet 1 dans "quelque part" et 2 dans le terminal, car 2 a été dupliqué de 1 AVANT que 1 n'ait été changé.

L'autre ordre:

command  1>somewhere 2>&1

redirigera d'abord fd 1vers somewhere, puis copiera cette même référence dans fd 2, donc à la fin 2 pointe également vers somewhere. Mais ils ne sont plus "liés" désormais. Chacun peut toujours être redirigé séparément.

ex:

command  1>somewhere 2>&1
exec 2>/dev/null

À la fin de celui-ci, fd 1pointe vers somewhere, et fd 2est dirigé vers/dev/null

Les noms usuels pour fd 1sont STDOUT (sortie standard), et le nom usuel pour fd 2est STDERR (erreur standard, car il est couramment utilisé pour afficher les erreurs sans interférer avec STDOUT)

Olivier Dulac
la source
@ Michael-mrozek: merci pour l'édition, mais j'insiste sur le fait de dire "copier" au lieu de "dupliquer" car "dupliquer" pourrait faire croire que désormais les deux sont "la même chose", ce qui n'est pas vrai. ex:: cmd 1>somewhere 2>&1 ; exec 2>/dev/nullaprès l'exec, seulement 2 ont été redirigés vers / dev / null (1 va toujours "quelque part"). J'ai besoin d'aide pour trouver un moyen de dire "ce que 1 pointe vers" au lieu de "le fd 1", cependant ... car c'est aussi déroutant ...
Olivier Dulac
1
Je ne sais pas ce que tu veux dire; c'est vous qui l'avez changé de "copier" à "dupliquer". Je n'ai fait que capitaliser et formater les choses, je n'ai pas changé un mot
Michael Mrozek
doh ... ^^ désolé. Et j'ai retravaillé à nouveau pour reformuler pour rendre plus précis ce qui est copié dans quoi ^^
Olivier Dulac
1

Je pense que la partie déroutante ici est la mauvaise compréhension que la redirection de stderr vers stdout connecte réellement les deux flux.

Une idée parfaitement raisonnable, mais ce qui se passe lorsque vous écrivez 2>&1est stderr prend un coup d'oeil à ce que stdout écrit et écrit au même endroit lui-même. Par conséquent, si vous dites par la suite à stdout d'aller écrire ailleurs, cela n'a aucun effet sur la destination de stderr qui a déjà été déplacée.

Je pense que c'est un peu contre-intuitif moi-même mais c'est comme ça que ça fonctionne. Configurez où vous voulez écrire en premier, puis dites à tout le monde "copiez-moi". J'espère que cela clarifie ...

jrichemont
la source
0

REPRODUCTION...

est important, mais plutôt dans le sens où il est source de beaucoup de confusion . C'est vraiment assez simple. Cette réponse n'est qu'une illustration "radicale".

La réponse acceptée est bonne, mais trop longue et met l'accent sur la "duplication".

Le Q se termine sagement par:

L' utilisation du mot duplication me semble un peu étrange dans ce contexte. Peut-être que cela me jette.

J'utilise la notation bash et je définis les variables "un" et "deux" comme descripteurs de fichiers "1" et "2". L'opérateur de redirection (de sortie) >est une affectation =. &et $signifie "valeur" de.

Les exemples man bash (avec le "1" par défaut ajouté)

ls 1>dirlist 2>&1      # both to dirlist
ls 2>&1 1>dirlist      # 1 to dirlist, 2 stays on tty/screen 

devenir:

one=dirlist  two=$one

et

two=$one   one=dirlist

Et même cela n'est pas automatique pour moi, et pour certains autres, je suppose. La première ligne vous laisse avec $oneet $twocontenant les deux "dirlist". Bien sûr.

La deuxième ligne commence par une affectation inutile. Les deux commencent par définition avec "ATS" (un peu symbolique) comme direction ; aucune valeur n'est modifiée par cette affectation, et avec des variables comme avec des descripteurs de fichiers, rien n'est lié par magie. La variable twon'est pas affectée par les éléments suivants one=dirlist. Bien sûr que non.

Sombody ici (il y a 6 ans) a suggéré "pointer vers" au lieu de "copier" ou "dupliquer", puis s'est rendu compte: ce serait déroutant aussi.

Cette duplication ou sémantique de pointeur n'est même pas nécessaire. C'est peut-être l'esperluette qui a besoin de plus d'attention. Opérateur / jeton / "valeur de".

Si - et seulement si - vous cherchez un moyen d'obtenir un numéro de travail surprenant sur votre console , puis un message "terminé" plus en prime un fichier nommé "2", alors vous allez:

ls 1>2& 2>/dev/null

Il se lit naturellement comme " copier" / "dupliquer" 1 à 2, puis les deux ensemble pour annuler . Mais l'idée est fausse, et aussi la syntaxe. (mais pas d'erreur de syntaxe, c'est valide)

La bonne façon de le planifier est de rediriger l'un des deux vers null, puis de rediriger l'AUTRE vers le MÊME endroit:

ls 1>/dev/null 2>&1
# or 
ls 2>/dev/null 1>&2

(le premier "1" peut être laissé de côté)

(OK l'acc. A n'est pas trop long, mais fait trop partie d'une liste - ou: très bonne visualisation, pas si bonne explication)

rastafile
la source