Linux sans magie #24 — stdin, stdout, stderr : comprendre les flux, les redirections et les pipes

Comprendre les trois flux standard d’un processus Linux, rediriger une entrée ou une sortie, séparer les erreurs et relier des commandes avec un pipe.

Une grande partie de la puissance du shell Linux repose sur une idée très simple : une commande peut lire des données, produire un résultat et produire des erreurs sur des flux séparés.

Quand on tape :

ls /etc

la commande écrit normalement son résultat dans le terminal. Quand on tape :

ls /chemin/inexistant

elle écrit un message d’erreur, lui aussi visible dans le terminal. Les deux apparaissent au même endroit, mais le shell sait qu’il s’agit de deux flux différents.

Comprendre cette séparation permet ensuite de lire des constructions comme :

commande > sortie.log 2> erreurs.log

ou :

commande 2>&1 | grep motif

sans les apprendre par cœur.

L’objectif de cet article est de comprendre les trois flux standard — stdin, stdout et stderr — puis d’utiliser les redirections et les pipes de manière prévisible.

Les trois flux standard

Lorsqu’un processus démarre, il dispose généralement de trois descripteurs de fichier déjà ouverts :

0  stdin   entrée standard
1  stdout  sortie standard
2  stderr  sortie d’erreur standard

Ces numéros sont importants, car les redirections du shell utilisent directement ces descripteurs.

Dans un terminal interactif classique :

  • stdin lit depuis le clavier ;
  • stdout écrit dans le terminal ;
  • stderr écrit également dans le terminal.

Le fait que stdout et stderr soient tous deux affichés à l’écran ne signifie donc pas qu’ils sont identiques.

Ils sont séparés pour permettre, par exemple, de sauvegarder la sortie normale dans un fichier tout en laissant les erreurs visibles.

Observer stdout et stderr séparément

Commençons par une commande qui produit une sortie normale :

printf 'bonjour\n'

Le texte bonjour est envoyé sur stdout.

Prenons maintenant une commande qui provoque volontairement une erreur :

ls /chemin/inexistant

Le message correspondant est envoyé sur stderr.

Pour voir la différence, redirigeons uniquement la sortie normale :

ls /etc /chemin/inexistant > sortie.txt

Le contenu correspondant à /etc est écrit dans sortie.txt, tandis que le message concernant /chemin/inexistant reste visible dans le terminal.

Vérifiez le fichier :

head sortie.txt

Le principe est déjà visible : > a modifié la destination de stdout, mais pas celle de stderr.

Rediriger stdout avec >

La redirection la plus courante est :

commande > fichier

Sans numéro explicite, une redirection commençant par > concerne stdout, donc le descripteur 1.

Ces deux écritures expriment donc la même idée :

printf 'test\n' > resultat.txt
printf 'test\n' 1> resultat.txt

Attention : avec >, si le fichier existe déjà, son contenu est normalement remplacé.

Vous pouvez le vérifier sans risque avec un fichier de laboratoire :

printf 'ancienne ligne\n' > demo.txt
printf 'nouvelle ligne\n' > demo.txt
cat demo.txt

Le fichier ne contient plus que :

nouvelle ligne

C’est un comportement important à retenir avant de rediriger la sortie vers un fichier utile.

Ajouter avec >>

Pour ajouter une sortie à la fin d’un fichier sans remplacer son contenu, utilisez :

commande >> fichier

Exemple :

printf 'ligne 1\n' > journal.txt
printf 'ligne 2\n' >> journal.txt
printf 'ligne 3\n' >> journal.txt
cat journal.txt

Résultat :

ligne 1
ligne 2
ligne 3

Comme pour >, l’absence de numéro signifie ici que la redirection concerne stdout.

Rediriger stderr avec 2>

Pour rediriger uniquement les erreurs, utilisez le descripteur 2 :

commande 2> erreurs.log

Exemple :

ls /etc /chemin/inexistant 2> erreurs.log

La liste de /etc reste affichée dans le terminal, tandis que le message d’erreur est écrit dans erreurs.log.

Vérifiez :

cat erreurs.log

Cette séparation est utile pour une commande automatisée : la sortie exploitable peut aller dans un fichier de résultats et les problèmes dans un fichier dédié.

Pour ajouter les erreurs au lieu d’écraser le fichier :

commande 2>> erreurs.log

Rediriger stdout et stderr vers deux fichiers

Comme les flux sont indépendants, on peut les envoyer vers deux destinations différentes :

ls /etc /chemin/inexistant > sortie.log 2> erreurs.log

Après exécution :

head sortie.log
cat erreurs.log

On obtient ainsi deux fichiers distincts :

stdout → sortie.log
stderr → erreurs.log

C’est souvent plus pratique qu’un seul fichier mélangé lorsque l’on doit diagnostiquer un script ou une tâche planifiée.

Comprendre 2>&1

Une écriture très fréquente est :

2>&1

Elle signifie : faire du descripteur 2 une copie de la destination actuelle du descripteur 1.

Par exemple :

ls /etc /chemin/inexistant > tout.log 2>&1

Le shell procède de gauche à droite :

  1. > tout.log envoie stdout vers tout.log ;
  2. 2>&1 envoie ensuite stderr vers la même destination que stdout.

Les deux flux terminent donc dans tout.log.

Vérifiez :

head tout.log

La sortie normale et le message d’erreur sont maintenant mélangés dans le même fichier.

L’ordre des redirections compte

Comparez :

commande > fichier.log 2>&1

et :

commande 2>&1 > fichier.log

Ces deux lignes ne sont pas équivalentes.

Dans la première, stdout est d’abord redirigé vers le fichier, puis stderr est redirigé vers la destination actuelle de stdout. Les deux vont donc dans le fichier.

Dans la seconde, stderr est d’abord envoyé vers la destination actuelle de stdout, qui est encore le terminal. Ensuite seulement, stdout est redirigé vers le fichier.

Résultat : stdout va dans le fichier mais stderr reste dans le terminal.

C’est l’une des raisons pour lesquelles il est préférable de comprendre les descripteurs plutôt que de mémoriser une syntaxe isolée.

Rediriger stdin avec <

Le flux stdin, descripteur 0, peut lui aussi être redirigé.

Créons un petit fichier :

printf 'alpha\nbeta\ngamma\n' > mots.txt

Puis exécutons :

wc -l < mots.txt

La commande wc -l ne reçoit plus ses données depuis le clavier : son stdin provient du fichier mots.txt.

Elle affiche alors simplement :

3

On pourrait aussi écrire :

wc -l mots.txt

mais le résultat affiché n’est pas présenté exactement de la même manière, car dans ce cas wc connaît explicitement le nom du fichier reçu comme argument.

La redirection < est donc bien une modification du flux d’entrée, pas un simple remplacement syntaxique de tous les arguments fichier.

Le pipe | : relier stdout à stdin

Un pipe relie la sortie standard d’une commande à l’entrée standard de la suivante.

Exemple :

printf 'alpha\nbeta\ngamma\n' | wc -l

Le fonctionnement logique est :

printf
  stdout
    ↓
   pipe
    ↓
  stdin
wc -l

wc n’a pas besoin de savoir comment les données ont été produites. Il lit simplement son stdin.

C’est ce principe qui permet de construire des chaînes de petits outils spécialisés.

Autre exemple :

printf 'alpha\nbeta\ngamma\n' | grep 'a'

Ici, grep reçoit la sortie de printf comme entrée.

Un pipe ne transporte pas stderr par défaut

Point essentiel : le pipe classique | relie stdout de la commande de gauche au stdin de celle de droite.

stderr n’est pas inclus automatiquement.

Essayez :

ls /etc /chemin/inexistant | grep passwd

Le contenu de /etc passe dans grep, mais le message d’erreur concernant le chemin inexistant reste directement visible dans le terminal.

Si l’on veut inclure aussi les erreurs dans le pipe, on peut d’abord rediriger stderr vers stdout :

ls /etc /chemin/inexistant 2>&1 | grep -E 'passwd|inexistant'

Ici, 2>&1 est appliqué à la commande ls, puis son flux combiné entre dans le pipe.

Bash possède aussi l’opérateur |&, qui relie stdout et stderr de la commande de gauche au pipe. Dans cet article, nous gardons toutefois 2>&1 | comme forme explicative principale, car elle montre clairement ce qui arrive aux descripteurs.

Lab : suivre les flux pas à pas

Créez un répertoire temporaire de travail :

mkdir -p /tmp/linux-sans-magie-24
cd /tmp/linux-sans-magie-24

Étape 1 — stdout seulement

printf 'OK\n' > sortie.txt
cat sortie.txt

Étape 2 — stderr seulement

ls /chemin/inexistant 2> erreurs.txt
cat erreurs.txt

Étape 3 — séparer les deux flux

ls /etc /chemin/inexistant > sortie-ls.txt 2> erreurs-ls.txt

Vérifiez :

head sortie-ls.txt
cat erreurs-ls.txt

Étape 4 — les regrouper

ls /etc /chemin/inexistant > combine.log 2>&1

Puis :

grep -E 'passwd|inexistant' combine.log

Étape 5 — utiliser un pipe

printf 'alpha\nbeta\ngamma\n' | grep 'a' | wc -l

Chaque commande reçoit les données produites par la précédente.

Ne pas confondre redirection et pipe

Ces deux constructions déplacent des données, mais elles ne servent pas exactement au même objectif.

Une redirection écrit ou lit généralement depuis un fichier :

commande > fichier

Un pipe relie deux processus :

commande1 | commande2

On peut évidemment combiner les deux :

commande1 | commande2 > resultat.txt

Ici :

stdout de commande1
        ↓
      pipe
        ↓
stdin de commande2
        ↓
stdout de commande2
        ↓
resultat.txt

Vérification et diagnostic

Quand une ligne de shell devient difficile à lire, décomposez-la par flux.

Pour :

commande > resultat.log 2> erreurs.log

posez-vous simplement deux questions :

où va stdout ? → resultat.log
où va stderr ? → erreurs.log

Pour :

commande 2>&1 | grep motif

le raisonnement devient :

stderr rejoint stdout
        ↓
stdout combiné entre dans le pipe
        ↓
grep reçoit le tout sur stdin

Cette méthode est beaucoup plus fiable que d’essayer de reconnaître visuellement une suite de symboles.

Sécurité et précautions

La redirection > peut écraser un fichier existant. Utilisez donc un chemin explicite et vérifiez la destination avant une commande importante.

Évitez par exemple d’exécuter mécaniquement une redirection vers un fichier de configuration système :

commande > /etc/fichier.conf

si vous n’avez pas vérifié précisément ce que produit la commande.

Pour les tests, préférez un répertoire temporaire ou un fichier dédié.

Les fichiers de logs peuvent aussi contenir des informations sensibles. Rediriger stdout et stderr vers un fichier ne supprime pas les secrets éventuellement produits par une commande ; cela change seulement leur destination.

Ce que nous ne couvrons pas encore

Cet article reste volontairement centré sur les mécanismes fondamentaux.

Nous ne détaillons pas ici :

  • les here-documents << ;
  • les here-strings <<< ;
  • la process substitution <(...) et >(...) ;
  • les descripteurs personnalisés comme 3> ;
  • exec pour modifier durablement les descripteurs d’un shell ;
  • tee ;
  • les subtilités avancées de pipefail ;
  • les pipelines complexes de traitement de texte.

Ces outils deviennent beaucoup plus simples une fois stdin, stdout, stderr et les redirections de base compris.

Nettoyage du lab

Depuis n’importe quel répertoire :

rm -rf /tmp/linux-sans-magie-24

Cette commande ne vise que le répertoire temporaire créé explicitement pour le lab.

Références officielles

Conclusion

Les symboles >, >>, <, 2>, 2>&1 et | ne sont pas des raccourcis mystérieux. Ils décrivent simplement la manière dont le shell connecte les flux d’un processus.

Le modèle à retenir est :

0 = stdin
1 = stdout
2 = stderr

Puis :

>   redirige stdout
>>  ajoute stdout à un fichier
<   redirige stdin
2>  redirige stderr
2>&1 envoie stderr vers la destination actuelle de stdout
|   relie stdout d’une commande à stdin de la suivante

Une fois ce modèle acquis, les pipelines deviennent plus faciles à lire et à construire. C’est précisément ce qui permettra, dans l’article suivant, d’utiliser grep, cut, sort, uniq et wc comme de petites briques combinables plutôt que comme une collection de commandes isolées.