lundi 18 octobre 2010

stderr, stdout et redirections

En vue de réaliser quelques expériences, nous créons un dossier test puis nous nous y rendons:

[toto@rigel ~]$ mkdir test
[toto@rigel ~]$ cd test
[toto@rigel test]$

Définissons l'alias ll pour le cas où il n'existerait pas:

[toto@rigel test]$ alias ll='ls -l'
[toto@rigel test]$

Commençons les expériences:

[toto@rigel test]$ touch toto 
[toto@rigel test]$ ll toto aime titine
ls: ne peut accéder aime: Aucun fichier ou dossier de ce type
ls: ne peut accéder titine: Aucun fichier ou dossier de ce type
-rw-rw-r--. 1 toto toto 0 oct 17 11:11 toto
[toto@rigel test]$

Nous créons le fichier toto et demandons via la commande ll des informations sur les fichiers toto, aime et titine. L'ordre d'arrivée à l'écran des communications envoyées par ll est assez interpelant. On s'attendrait à trouver quelque chose comme:

-rw-rw-r--. 1 toto toto 0 oct 17 11:11 toto
ls: ne peut accéder aime: Aucun fichier ou dossier de ce type
ls: ne peut accéder titine: Aucun fichier ou dossier de ce type

ce qui correspond à l'ordre des arguments de la commande.
L'expérience montre que les messages arrivent sur l'écran par deux canaux différents dont l'un, celui par lequel passent les messages d'erreur, est cette fois prioritaire. Mais attention, ce n'est pas toujours le cas et si l'ordre naturel avait été respecté, on n'aurait rien pu en déduire.

Interceptons le flux sortant de ll par un pipe afin de le faire traiter par le programme tr. Le traitement consistera (par exemple) à remplacer tous les "t" par des "b".

[toto@rigel test]$ ll toto aime titine | tr t b
ls: ne peut accéder aime: Aucun fichier ou dossier de ce type
ls: ne peut accéder titine: Aucun fichier ou dossier de ce type
-rw-rw-r--. 1 bobo bobo 0 ocb 17 11:11 bobo
[toto@rigel test]$

Nous constatons que les messages d'erreur ne sont pas passés par la moulinette tr. Il y a bien deux canaux. Le canal par lequel transite l'output normal est la sortie standard (stdout). L'autre canal, pour les messages d'erreur est l'erreur standard (stderr). Le pipe à intercepté uniquement stdout.
Ce qu'il advient de l'output normal après l'exécution d'une commande est géré par le descripteur 1. A ce descripteur correspond un canal, le canal 1 (stdout). De même, ce qu'il advient des messages d'erreur est géré par le descripteur 2 auquel correspond le canal 2 (stderr).
On peut rediriger stderr vers stdout, comme ceci:

[toto@rigel test]$ ll toto aime titine 2>&1 | tr t b
ls: ne peub accéder aime: Aucun fichier ou dossier de ce bype
ls: ne peub accéder bibine: Aucun fichier ou dossier de ce bype
-rw-rw-r--. 1 bobo bobo 0 ocb 17 11:11 bobo
[toto@rigel test]$

L'instruction 2>&1 affecte au descripteur 2 le canal correspondant au descripteur 1.
En clair, stderr est redirigé vers stdout. Grâce au traitement par tr, on constate que cette fois, contrairement à ce qui se passait dans le cas précédent, la totalité des messages transite par le pipe.
Maintenant, nous aimerions trouver comment procéder pour que seul stderr transite par le pipe.
Tout d'abord, montrons que dans le cas de plusieurs redirections successives , l'ordre dans lequel elles interviennent est primordial:

[toto@rigel test]$ ll toto aime titine > info 2>&1
[toto@rigel test]$ cat info
ls: ne peut accéder aime: Aucun fichier ou dossier de ce type
ls: ne peut accéder titine: Aucun fichier ou dossier de ce type
-rw-rw-r--. 1 toto toto 0 oct 17 11:11 toto
[toto@rigel test]$

ce qui correspond au schéma:
L'instruction > info est mise pour 1> info (1 est le descripteur de fichier par défaut).
Elle affecte au descripteur 1 le fichier info: stdout est redirigé vers info.
Ensuite 2>&1 affecte au descripteur 2 le canal correspondant au descripteur 1. Ce canal a déjà été changé. Ce n'est plus le canal 1 initial. En fait de canal, il s'agit maintenant du fichier info:
stderr est à son tour redirigé vers le fichier info.
Rien n'arrive sur l'écran.
Permutons les deux redirections:

[toto@rigel test]$ ll toto aime titine 2>&1 > info
ls: ne peut accéder aime: Aucun fichier ou dossier de ce type
ls: ne peut accéder titine: Aucun fichier ou dossier de ce type
[toto@rigel test]$ cat info
-rw-rw-r--. 1 toto toto 0 oct 17 11:11 toto
[toto@rigel test]$

Le schéma correspondant est celui- ci:

2>&1 affecte au descripteur 2 le canal 1 qui est le canal initial correspondant au descripteur 1. Celui-ci n'a pas encore changé: stderr est redirigé vers ce canal 1. Ensuite > info (c'est-à-dire 1> info) affecte au descripteur 1 le fichier info: stdout est redirigé vers info. En fait la première redirection ne sert absolument à rien car de toute façon, stderr serait arrivé à l'écran. Oui mais cette fois stderr arrive sur l'écran via le canal 1, ce qui nous donne l'occasion de le faire transiter par tr, comme représenté sur ce schéma:




Essayons la formule:

[toto@rigel test]$ ll toto aime titine 2>&1 > info | tr t b
ls: ne peub accéder aime: Aucun fichier ou dossier de ce bype
ls: ne peub accéder bibine: Aucun fichier ou dossier de ce bype
[toto@rigel test]$ cat info
-rw-rw-r--. 1 toto toto 0 oct 17 11:11 toto
[toto@rigel test]$

Bingo, ça fonctionne! Cependant, on voudrait une solution sans redirection vers un fichier, on voudrait que tout arrive à l'écran, non seulement stderr mais aussi stdout. Pour cela nous avons besoin d'un nouveau descripteur de fichier: 3.

[toto@rigel test]$ echo toto >&3
-bash: 3: Mauvais descripteur de fichier
[toto@rigel test]$

Et bien oui: on veut rediriger stdout vers le canal 3, c'est-à-dire affecter au descripteur 1, le canal correspondant au descripteur 3. Mais ce descripteur n'existe pas, pas encore.

[toto@rigel test]$ exec 3>&1
[toto@rigel test]$

Maintenant, il existe:

[toto@rigel test]$ echo toto >&3
toto
[toto@rigel test]$

Remarquons que

[toto@rigel test]$ echo toto >&3 | tr t r
toto
[toto@rigel test]$

Alors que

[toto@rigel test]$ echo toto | tr t r
roro
[toto@rigel test]$

Nous avons notre solution:

[toto@rigel test]$ ll toto aime titine 2>&1 1>&3 | tr t b
ls: ne peub accéder aime: Aucun fichier ou dossier de ce bype
ls: ne peub accéder bibine: Aucun fichier ou dossier de ce bype
-rw-rw-r--. 1 toto toto 0 oct 17 11:11 toto

qui correspond au schéma: