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 :
stdinlit 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 :
> tout.logenvoiestdoutverstout.log;2>&1envoie ensuitestderrvers la même destination questdout.
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>; execpour 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
- GNU Bash Reference Manual — Redirections — GNU Project
- GNU Bash Reference Manual — Pipelines — GNU Project
- GNU Bash Reference Manual — GNU Project
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.
