Que sont stdin, stdout et stderr sous Linux?



Les flux stdin, stdout et stderr sont des canaux de communication essentiels lors de l’exécution de commandes sous Linux. Ils permettent de gérer l’entrée, la sortie et les erreurs des processus. Découvrons comment les manipuler et les utiliser efficacement.

Les Points de Connexion des Flux

Lorsque vous explorez les systèmes Linux ou Unix, vous rencontrerez souvent les termes stdin, stdout et stderr. Il s’agit des trois flux de données standards qui sont établis lors du lancement d’une commande. En informatique, un flux est un mécanisme de transfert de données, et dans ce cas, ces données sont sous forme de texte.

À l’instar des cours d’eau, les flux de données ont deux points de terminaison : une source et une destination. La commande que vous lancez définit une extrémité de chaque flux. L’autre extrémité est déterminée par l’interpréteur de commandes (shell) qui a lancé la commande. Cette extrémité peut être connectée au terminal, à un tube (pipe), redirigée vers un fichier ou vers une autre commande, selon la ligne de commande utilisée.

Fonctionnement des Flux Standards Linux

Sous Linux, stdin représente le flux d’entrée standard. Il reçoit du texte comme données d’entrée pour la commande. Le flux stdout (sortie standard) transmet la sortie texte de la commande vers le shell. Les messages d’erreur générés par la commande sont acheminés par le flux stderr (erreur standard).

Ainsi, on observe deux flux de sortie, stdout et stderr, et un flux d’entrée, stdin. En ayant des voies de communication distinctes pour les messages d’erreur et la sortie normale, on peut les traiter de manière indépendante.

Les Flux, Traités comme des Fichiers

Dans Linux, les flux – comme la plupart des éléments du système – sont traités comme des fichiers. Vous pouvez lire du texte depuis un fichier et écrire du texte dans un fichier. Ces deux opérations impliquent un flux de données. Le concept de gestion d’un flux de données en tant que fichier est donc naturel et simple.

Chaque fichier manipulé par un processus est identifié par un numéro unique, appelé descripteur de fichier. C’est ce descripteur qui permet d’identifier un fichier spécifique lors de toute opération.

Les valeurs suivantes sont attribuées aux flux standards:

0 : stdin
1 : stdout
2 : stderr

Réaction aux Redirections et Tubes

Pour faciliter l’introduction d’un sujet, il est commun de commencer par une version simplifiée. Par exemple, en grammaire, on nous enseigne la règle du « i » avant « e », sauf après « c ». Cependant, il y a plus d’exceptions que de cas qui respectent cette règle.

De même, en parlant de stdin, stdout et stderr, on a tendance à simplifier en affirmant qu’un processus n’a pas connaissance de la destination de ses flux standard. Mais un processus devrait-il savoir si sa sortie va au terminal ou est redirigée vers un fichier? Peut-il savoir si son entrée vient du clavier ou d’un autre processus ?

En réalité, un processus le sait – ou peut le savoir s’il vérifie – et peut adapter son comportement si le développeur l’a prévu.

Ce changement de comportement est facile à observer. Testez ces deux commandes:

ls

ls | cat

La commande ls se comporte différemment si sa sortie (stdout) est redirigée vers une autre commande. C’est ls qui passe à une sortie en colonne unique, et non une transformation effectuée par cat. Et ls fait de même si sa sortie est redirigée:

ls > capture.txt

cat capture.txt

Redirection des Flux stdout et stderr

L’avantage d’avoir un flux dédié pour les messages d’erreur est qu’on peut rediriger la sortie normale (stdout) vers un fichier tout en continuant à voir les erreurs (stderr) dans le terminal. Ainsi, on peut réagir aux erreurs lorsqu’elles se produisent, et on évite de mélanger les messages d’erreur avec la sortie normale dans le fichier.

Créez un fichier nommé error.sh contenant le code suivant:

#!/bin/bash

echo "Tentative d'accès à un fichier inexistant"
cat bad-filename.txt

Rendez le script exécutable avec cette commande:

chmod +x error.sh

La première ligne du script affiche du texte dans le terminal via stdout. La deuxième ligne tente d’accéder à un fichier inexistant, ce qui générera un message d’erreur via stderr.

Exécutez le script:

./error.sh

On observe que les deux flux de sortie, stdout et stderr, sont affichés dans le terminal.

Essayons maintenant de rediriger la sortie vers un fichier :

./error.sh > capture.txt

Le message d’erreur, véhiculé par stderr, est toujours affiché dans le terminal. Vérifions le contenu du fichier pour voir si la sortie stdout y a bien été redirigée.

cat capture.txt

La sortie de stdout a été redirigée vers le fichier comme prévu.

Le symbole de redirection > cible par défaut le flux stdout. On peut aussi utiliser directement le descripteur numérique pour préciser le flux à rediriger.

Pour rediriger explicitement stdout, utilisez:

1>

Pour rediriger explicitement stderr, utilisez:

2>

Testons de nouveau, cette fois avec `2>`:

./error.sh 2> capture.txt

Le message d’erreur est redirigé, et le message d’echo stdout est affiché dans le terminal:

Le message stderr est bien présent dans capture.txt.

Redirection des Flux stdout et stderr vers Deux Fichiers Différents

Si on peut rediriger stdout et stderr de façon indépendante, on devrait pouvoir les rediriger en même temps vers deux fichiers différents, n’est-ce pas ?

En effet. Cette commande redirige stdout vers un fichier nommé capture.txt et stderr vers un fichier nommé error.txt.

./error.sh 1> capture.txt 2> error.txt

Les deux flux de sortie étant redirigés vers des fichiers, il n’y a plus de sortie visible dans le terminal. L’invite de commande réapparaît comme si rien ne s’était passé.

Vérifions les contenus des fichiers:

cat capture.txt
cat error.txt

Redirection de stdout et stderr vers le Même Fichier

On peut aussi envoyer les flux stdout et stderr vers un seul et même fichier.

Cela se fait avec la commande:

./error.sh > capture.txt 2>&1

Décortiquons cette commande:

./error.sh: lance le script error.sh.
> capture.txt: redirige le flux stdout vers le fichier capture.txt. > est un raccourci pour 1>.
2>&1: ceci redirige le flux 2, stderr, vers la même destination que le flux 1, stdout.

Il n’y a pas de sortie visible, ce qui est normal.

Vérifions le contenu de capture.txt.

cat capture.txt

Les flux stdout et stderr ont été redirigés vers un seul fichier.

Pour rediriger et supprimer silencieusement la sortie, utilisez `/dev/null`.

Détection de la Redirection dans un Script

Nous avons vu qu’une commande peut détecter si ses flux sont redirigés et adapter son comportement. On peut faire de même dans nos scripts, et c’est très simple à faire.

Créez un fichier nommé input.sh avec le code suivant:

#!/bin/bash

if [ -t 0 ]; then

  echo "stdin provient du clavier"

else

  echo "stdin provient d'un tube ou d'un fichier"

fi

Rendez le script exécutable :

chmod +x input.sh

La partie importante est le test entre crochets. L’option -t (terminal) renvoie `true` (0) si le fichier associé au descripteur se termine dans le terminal. On utilise ici le descripteur 0, qui représente stdin.

Si stdin est connecté au terminal, le test sera vrai. Si stdin est connecté à un fichier ou un tube, le test échouera.

Utilisons un fichier texte quelconque pour fournir l’entrée du script, par exemple `dummy.txt`.

./input.sh < dummy.txt

Le script reconnaît que l’entrée ne provient pas du clavier mais d’un fichier. On pourrait donc adapter le comportement du script en conséquence.

Essayons avec un tube:

cat dummy.txt | ./input.sh

Le script reconnaît que son entrée lui est fournie par un tube. Plus précisément, il détecte que le flux stdin n’est pas connecté au terminal.

Exécutons le script sans redirection ni tube:

./input.sh

Le flux stdin est connecté au terminal, et le script le signale.

Pour faire de même avec le flux de sortie, créons un nouveau script nommé output.sh:

#!/bin/bash

if [ -t 1 ]; then

echo "stdout est dirigé vers le terminal"

else

echo "stdout est redirigé ou utilisé dans un tube"

fi

Rendez-le exécutable :

chmod +x input.sh

La seule différence majeure avec le script précédent est le test entre crochets. On utilise le chiffre 1 pour représenter le descripteur de fichier stdout.

Testons-le. Redirigeons la sortie vers cat:

./output.sh | cat

Le script reconnaît que sa sortie ne va pas directement vers le terminal.

On peut tester en redirigeant la sortie vers un fichier:

./output.sh > capture.txt

Il n’y a pas de sortie dans le terminal, l’invite de commande réapparaît silencieusement, comme prévu.

Regardons le contenu du fichier capture.txt:

cat capture.txt

Le test de notre script détecte que le flux stdout n’est pas directement envoyé au terminal.

Enfin, exécutons le script sans redirection ni tube, pour vérifier qu’il détecte correctement que stdout va au terminal:

./output.sh

C’est bien le cas.

Conscience des Flux

Être capable de savoir si vos scripts sont connectés au terminal, à un tube ou en cours de redirection permet d’adapter leur comportement en conséquence.

La verbosité des logs et des informations de diagnostic peut être ajustée selon que la sortie est affichée à l’écran ou enregistrée dans un fichier. Les messages d’erreur peuvent être enregistrés dans un fichier séparé de la sortie normale.

En général, plus de connaissances donnent plus de possibilités d’action.