
Votre terminal n'est pas un terminal : Introduction aux flux
- 20 minsArticle d’origne publié par Lucas Fernandes da Costa sous Licence Copyleft en avril 2019. Traduction en français par mes soins également sous licence Copyleft
J’aime les flux parce que je n’aime pas les logiciels.
J’essaie toujours de construire moins de logiciels. Moins de logiciels signifie que vous passez moins de temps à le mettre à jour, moins de temps à le réparer et moins de temps à y penser. La seule chose meilleure que « moins de logiciels », c’est pas de logiciel du tout.
Les flux (Streams) nous aident à écrire moins de logiciels parce qu’ils permettent aux programmes de communiquer entre eux.
Si les programmes ne peuvent pas communiquer ils doivent alors avoir un très grand nombre de fonctionnalités pour satisfaire les besoins de leurs utilisat⋅rices⋅eurs, créant ainsi plus de logiciels. En permettant la communication entre les processus, les flux incitent à des logiciels plus petits et peuvent même parfois éviter leur conception.
Apprendre à connaître les flux vous aide à mieux comprendre le fonctionnement des systèmes UNIX et à simplifier votre environnement de développement.
Ce que sont les Flux
Les flux ne sont que ça : des flux. De la même façon qu’une rivière a un flux d’eau, les programmes ont des flux de données. De plus, tout comme vous pouvez utiliser des canalisations pour transporter l’eau d’un endroit à un autre, vous pouvez utiliser des tuyaux UNIX pour transporter des données d’un programme à un autre. C’est cette analogie qui a inspiré la conception des flux :
“We should have some ways of connecting programs like a garden hose — screw in another segment when it becomes necessary to massage data in another way. This is the way of I/O also”. — Douglas McIlroy
Les flux peuvent être utilisés pour transférer des données vers des programmes et pour en extraire des données.
Sous UNIX, les programmes reçoivent certains flux qui leur sont attachés par défaut, à la fois en entrée et en sortie. Nous appelons ces flux standard.
Il existe trois flux standard différents :
stdin
ou entrée standard, le flux qui alimente votre programme en donnéesstdout
ou sortie standard, le flux dans lequel votre programme écrit sa sortie principalestderr
ou erreur standard, le flux dans lequel votre programme écrit ses messages d’erreur
Le programme fortune, par exemple, écrit quelques morceaux de sagesse dans le flux stdout
.
$ fortune
It is simplicity that is difficult to make
-- Bertold Brecht
Quand fortune s’est exécuté, il s’est attaché à stdin
, stdout
et stderr
. Puisqu’il n’a pas produit d’erreurs et n’a pas reçu d’entrée externe, il a juste écrit sa sortie dans stdout
.
fortune
. À gauche il y a une flèche avec stdin
écrit dessus. A droite il y a deux flèches, l'une avec stdout
et l'autre avec stderr
cowsay
est un autre programme qui écrit à stdout
. cowsay
prend une chaine de caractère (string) et affiche une vache l’exprimant.
$ cowsay "Brazil has a decent president"
_______________________________
< Brazil has a decent president >
-------------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
Contrairement à fortune
, cowsay
ne dit pas des choses forcément intelligentes − comme nous venons de le voir. Heureusement, nous pouvons alimenter le flux stdin
qui y est rattaché.
Tout ce que nous avons à faire pour rendre cowsay
plus intelligent et répéter les citations de fortune
est d’utiliser ce que nous appelons un tuyau − représenté par |
− pour attacher le stdout
de fortune
au stdin
du cowsay
.
$ fortune | cowsay
_________________________________________
/ A language that doesn't have everything \
| is actually easier to program in than |
| some that do. |
| |
\ -- Dennis M. Ritchie /
-----------------------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
Nous utilisons des tuyaux pour connecter le flux de sortie d’un programme au flux d’entrée d’un autre programme.
fortune
et à cowsay
, mais montrant cette fois la flèche stdout
de fortune
pointant vers la flèche stdin
qui va dans la case cowsay
.Vous pouvez voir la sortie de cowsay
sur votre écran car, par défaut, votre terminal reçoit les flux standard stdin
, stdout
et stderr
qui y sont attachés.
Les données entrent par stdout
et stderr
et sortent par l’autre extrémité : votre moniteur (le terminal). De la même façon, l’entrée de votre clavier passe par stdin
vers un programme.
Le programme cat
, par exemple, utilise le stdin
pour recevoir les entrées de votre clavier et le stdout
pour les envoyer en sortie :
$ cat
Everything I write before pressing Enter
Everything I write before pressing Enter
Gets logged right after
Gets logged right after
stdin
et passant par cat
, stdout
, et stderr
vers un moniteur.Nous pouvons faire plus élaboré en utilisant sed
pour remplacer toutes les occurrences de I
par We
chaque fois que nous appuyons sur Entrer
:
$ cat | sed -E "s/I/We/"
I think streams are quite cool.
We think streams are quite cool.
stdin
vers cat
et sortant par sed
, sortie qui entre dans le stdin
de sed
, sed
fonctionnant à son tour, avec un stdout
de sed
vers à un moniteur.Aussi, au cas où vous ne le sauriez pas, sed
est un éditeur de flux (stream editor
).
Comment les flux communiquent avec votre « terminal » ?
Beaucoup de sites Web mis en lien externes dans au dernier billet de blog que j’ai écrit. Dans la section commentaires de l’un d’eux, des personnes on fait remarquer que je n’utilisais pas vraiment de terminal.
Ils avaient tout à fait raison avec leurs commentaires pas-du-tout-pédants. Cependant, voici une photo de moi en 1978 − un peu avant ma naissance − utilisant un terminal série HP 2647A :
Si vous n’êtes pas un voyageur du temps pur et dur comme moi, ce que vous utilisez n’est qu’un émulateur de terminal. Qui aurait pu deviner, n’est-ce pas ?
Les émulateurs de terminaux sont des simulations logicielles de terminaux « réels ». Ces émulateurs vous fournissent une interface pour interagir avec le pilote TTY de Linux1. Le pilote du TTY est responsable de la manipulation des données en provenance et à destination des programmes.
Chaque TTY a ses propres flux stdin
, stdout
et stderr
qui lui sont connectés. Ce sont les flux fournis aux programmes pour qu’ils lisent (stdin
) et écrivent (stdout
et stderr
).
Voici une version plus précise de ce qui s’est passé lorsque vous avez lancé cat | sed -E "s/I/We/"
dans le dernier exemple :
cat
, sed
et enfin le retour vers un moniteur.Comme tout sous UNIX, le tty
est un fichier. Chaque instance d’un émulateur de terminal a un fichier tty
différent qui lui est associé. Parce que chaque émulateur lit et écrit dans un fichier différent, vous ne voyez pas le résultat des programmes que vous exécutez dans toutes les fenêtres que vous avez ouvertes.
Pour savoir quel tty
est associé à une fenêtre de terminal on peut utiliser la commande tty
:
tty
dans chacune d'elles montre le chemin vers deux fichiers différents en sortie.Lorsque vous ouvrez une nouvelle fenêtre de terminal, c’est vers cela que pointent ses flux :
stdout
, stderr
et stdin
et trois flèches pointant vers la droite vers trois autres cases avec /dev/ttys/005
écrites dessus.Dans l’image ci-dessus, le /dev/ttys/005
n’est qu’un exemple. Cela aurait pu être n’importe quel autre fichier car il y en aura un nouveau pour chaque instance tty
.
Redirection
Pour écrire la sortie d’un programme dans un fichier au lieu du tty
, vous pouvez diriger le flux stdout
ailleurs.
Dans l’exemple ci-dessous, nous écrivons le contenu du répertoire /
dans le fichier content_list.txt
du dossier /tmp
. Nous le faisons en utilisant l’opérateur >
, ce qui nous permet de rediriger le flux stdout
par défaut.
$ ls / 1> /tmp/content_list.txt
Pour vérifier ce qu’il y a dans /tmp/content_list.txt
, vous pouvez maintenant utiliser cat
:
$ cat /tmp/content_list.txt
Applications
Library
Network
System
Users
Volumes
bin
cores
dev
etc
home
net
private
sbin
themes
tmp
usr
var
C’est différent de ce qu’aurait fait ls /
si vous l’aviez utilisée, la commande ls
n’aurait rien écrit sur votre terminal. Au lieu d’écrire dans le fichier /dev/tty
que votre émulateur de terminal lit, elle a écrit dans /tmp/content_list.txt
.
ls
. Initialement, le stdout
pointe vers /dev/tty
mais après avoir fait une redirection, il pointe vers le chemin du fichier.On peut obtenir le même effet de redirection en utilisant >
au lieu de 1>
.
$ ls / > /tmp/content_list.txt
L’omission du numéro préfixé fonctionne parce que le 1
devant >
indique le flux que nous voulons rediriger. Dans ce cas, 1
est le descripteur de fichier pour stdout
.
Comme le tty
n’est qu’un fichier, vous pouvez aussi rediriger un flux stdout
d’un terminal vers un autre.
cowsay
transférée d'un terminal à l'autre en la redirigeant vers le fichier TTY de l'autre terminal.Si nous voulions rediriger le flux stderr
, nous pourrions préfixer son descripteur de fichier (file-descriptor) − qui est 2
− vers >
.
$ cat /this/path/does/not/exist 2> /tmp/cat_error.txt
cat
avec une redirection du flux stderr
. Initialement, le </code>stderr</code> pointe vers /dev/tty
, mais après avoir fait une redirection, il pointe vers le chemin du fichier.Maintenant le fichier /tmp/cat_error.txt
contient tout ce que cat
a écrit dans stderr
.
$ cat /tmp/cat_error.txt
cat: /this/path/does/not/exist: No such file or directory
Pour rediriger à la fois stdin
et stderr
nous pouvons utiliser &>
.
$ cat /does/not/exist /tmp/content_list.txt &> /tmp/two_streams.txt
Maintenant /tmp/two_streams
contiendra ce qui a été écrit dans stdout
et stderr
.
$ cat /tmp/two_streams.txt
cat: /does/not/exist: No such file or directory
Applications
Library
Network
System
Users
Volumes
bin
cores
dev
etc
home
installer.failurerequests
net
private
sbin
themes
tmp
usr
var
stdout
et stderr
pointant vers /dev/tty
ils pointent vers le chemin du fichier.Vous devez être prudent lorsque vous écrivez dans un fichier avec >
. L’utilisation d’un simple >
remplace le contenu d’un fichier.
$ printf "Look, I have something inside" > /tmp/careful.txt
$ cat /tmp/careful.txt
Look, I have something inside
$ printf "Now I have something else" > /tmp/careful.txt
$ cat /tmp/careful.txt
Now I have something else
Pour ajouter à un fichier au lieu d’écraser son contenu, vous devez utiliser >>
.
$ printf "Look, I have something inside" > /tmp/careful.txt
$ cat /tmp/careful.txt
Look, I have something inside
$ printf "\nNow I have one more thing" >> /tmp/careful.txt
$ cat /tmp/careful.txt
Look, I have something inside
Now I have one more thing
Pour la lecture à partir de stdin
, on peut utiliser l’opérateur <
.
La commande suivante utilise le flux stdin
pour alimenter sed
avec le contenu de /usr/share/dict/words
. sed
sélectionne ensuite une ligne aléatoirement et l’écrit dans stdout
.
$ sed -n "${RANDOM}p" < /usr/share/dict/words
alloestropha
Puisque le descripteur de fichier de stdin est 0
, nous pouvons obtenir le même effet en le préfixant à <
.
$ sed -n "${RANDOM}p" 0< /usr/share/dict/words
pentameter
Il est également important de noter la différence entre l’utilisation d’opérateurs de redirection et de tuyaux (NDLR: pipes |
). Lorsque nous utilisons des tuyaux, nous attachons la sortie stdout
d’un programme au stdin
d’un autre programme. Lorsque nous utilisons la redirection, nous changeons l’emplacement vers lequel un flux spécifique pointe au démarrage d’un programme.
Puisque les flux ne sont que des descripteurs de fichiers, nous pouvons créer autant de flux que nous le voulons. Pour cela, nous pouvons utiliser exec
pour ouvrir des fichiers sur des descripteurs de fichiers spécifiques.
Dans l’exemple ci-dessous, nous ouvrons /usr/share/dict/words
pour lire sur le descripteur 3
.
$ exec 3< /usr/share/dict/words
stdin
, stdout
, et stderr
et ils pointent vers le fichier tty. Le descripteur de fichier 3 pointe vers /usr/share/dict/words
Maintenant nous pouvons utiliser ce descripteur comme stdin
pour un programme en utilisant <&
.
$ sed -n "${RANDOM}p" 0<&3
dactylic
Ce que fait l’opérateur <&
dans l’exemple ci-dessus est de dupliquer le descripteur de fichier 3
et d’en faire une copie à 0
(stdin
).
/usr/share/dict/words
. Le descripteur 0 (stdin
) pointe vers le même fichier.Une fois que vous avez ouvert un descripteur de fichier pour la lecture, vous ne pouvez le « consommer » qu’une seule fois. D’où la raison pour laquelle tenter d’utiliser 3
une nouvelle fois ne fonctionnera pas :
$ grep dactylic 0<&3
Pour fermer un descripteur de fichier nous pouvons utiliser -
, comme si nous le copiions dans le descripteur de fichier que nous voulons fermer.
$ exec 3<&-
Tout comme nous pouvons utiliser <
pour ouvrir un fichier en lecture, nous pouvons utiliser >
pour ouvrir un fichier en écriture.
Dans l’exemple ci-dessous, nous créons un fichier appelé output.txt
, l’ouvrons en mode écriture, et dupliquons son descripteur vers 4
:
$ touch /tmp/output.txt
$ exec 4>&/tmp/output.txt
stdin
, stdout
, et stderr
et ils pointent vers le fichier tty. Le descripteur de fichier 4 pointe vers /tmp/output.txt
Maintenant si nous voulons que cowsay
écrive dans le fichier /tmp/output.txt
, nous pouvons dupliquer le descripteur de fichier depuis 4
et le copier dans 1
(stdout
)
$ cowsay "Does this work?" 1>&4
$ cat /tmp/output.txt
_________________
< Does this work? >
-----------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
/usr/share/dict/words
. Le descripteur 0 (stdin
) pointe vers le même fichier.Intuitivement, pour ouvrir un fichier en lecture et écriture, vous pouvez utiliser <>
. Tout d’abord, créons un fichier appelé /tmp/lines.txt
, ouvrons un descripteur r/w
pour lui et copions-le dans 5
.
$ touch /tmp/lines.txt
$ exec 5<> /tmp/lines.txt
stdin
, </code>stdout</code>, et stderr
et ils pointent vers le fichier tty. Le descripteur de fichier 5 points à /tmp/lines.txt
Dans l’exemple ci-dessous, nous copions les 3 premières lignes de /usr/share/dict/propernames
dans /tmp/lines.txt
.
$ head -n 3 /usr/share/dict/propernames 1>&5
$ cat /tmp/lines.txt
Aaron
Adam
Adlai
Notez que si nous essayions de lire à partir de 5
avec cat
, nous n’obtiendrions aucun résultat car lorsque nous avons écrit, nous progressions dans le fichier et 5
est désormais sa limite finale.
$ cat 0<&5
Nous pouvons résoudre ce problème en fermant le 5
et en le rouvrant.
$ exec 5<&-
$ exec 5<> /tmp/lines.txt
$ cat 0<&5
Aaron
Adam
Adlai
Postscriptum
Lors de la génération de nombres aléatoires
Dans les exemples ci-dessus, j’ai utilisé $RANDOM
pour générer des nombres aléatoires et les passer à sed
afin de sélectionner des lignes aléatoires dans le fichier /usr/share/dict/words
.
Vous avez peut-être remarqué que cela vous donne habituellement des mots commençant par a
, b
ou c
. C’est parce que RANDOM
est long deux octets et ne peut donc aller que de 0
à 32,767
.
Le fichier /usr/share/dict/words
compte 235 886 lignes.
$ wc -l /usr/share/dict/words
235886 /usr/share/dict/words
Puisque le plus grand nombre possible généré par RANDOM
est environ 7 fois plus petit que /usr/share/dict/words
, il n’est pas approprié de sélectionner des mots au hasard. Dans ce billet, il a été utilisé simplement par souci de simplicité.
Sur les appareils TTY et I/O
J’ai intentionnellement omis quelques détails en expliquant que le TTY et l’émulateur de terminal se trouvent entre les périphériques d’I/O et les processus.
Vous trouverez une explication beaucoup plus complète et approfondie de toutes les composantes de ce processus de communication dans ce post extraordinaire de Linus Åkesson intitulé “The TTY Demystified”.
Références et liens utiles
- Bash One-Liners Explained, Part III: All about redirections by Peter Krumins. This is definitely a must-read. Excellent post.
- The TTY demystified by Linus Åkesson is also a must-read, as I’ve mentioned above.
- An overview of pipes and FIFOs from the Linux
man pages
- Introduction to Linux: A Hands On Guide - Chapter 5. I/O redirection by Machtelt Garrels
- What is a file descriptor - Computer Hope
- The
man page
forbash
- The Illustrated Redirection Tutorial on the Bash Hackers Wiki
- The
man page
for the standard streams - An explanation on duplicating file descriptors
Lucas Fernandes da Costa at London, United Kingdom − Licence Copyleft en avril 2019 .
Remerciements
- Dada : pour avoir pris du temps et (me) rassurer
- Mily1000V : pour les discussions autour des traductions
- Lucas F. Costa : pour le texte original sous licence copyleft
Notes et commentaires
-
TTY est parfois traduit en système de téléimprimeur (ATS), notamment au Quèbec http://www.thesaurus.gouv.qc.ca/tag/terme.do?id=12371 ↩
Merci à toutes les personnes qui soutiennent les efforts par leurs dons

Xavier Coadic
Human Collider