stdin
, stdout
et stderr
sont des flux attachés aux descripteurs de fichier 0, 1 et 2 respectivement d'un processus.
À l'invite d'un shell interactif dans un terminal ou un émulateur de terminal, tous ces 3 descripteurs de fichier feraient référence à la même description de fichier ouverte qui aurait été obtenue en ouvrant un fichier de terminal ou de périphérique pseudo-terminal (quelque chose comme /dev/pts/0
) en lecture + écriture mode.
Si à partir de ce shell interactif, vous démarrez votre script sans utiliser de redirection, votre script héritera de ces descripteurs de fichier.
Sous Linux, /dev/stdin
, /dev/stdout
, /dev/stderr
sont des liens symboliques vers /proc/self/fd/0
, /proc/self/fd/1
, /proc/self/fd/2
respectivement, se symlinks spéciales au fichier réel qui est ouvert sur les descripteurs de fichiers.
Ce ne sont pas stdin, stdout, stderr, ce sont des fichiers spéciaux qui identifient les fichiers vers lesquels stdin, stdout, stderr vont (notez que c'est différent dans d'autres systèmes que Linux qui ont ces fichiers spéciaux).
lire quelque chose depuis stdin signifie lire depuis le descripteur de fichier 0 (qui pointera quelque part dans le fichier référencé par /dev/stdin
).
Mais dans $(</dev/stdin)
, le shell ne lit pas depuis stdin, il ouvre un nouveau descripteur de fichier pour lire sur le même fichier que celui ouvert sur stdin (donc lire depuis le début du fichier, pas là où stdin pointe actuellement).
Sauf dans le cas particulier des terminaux ouverts en mode lecture + écriture, stdout et stderr ne sont généralement pas ouverts à la lecture. Ils sont censés être des flux sur lesquels vous écrivez . La lecture du descripteur de fichier 1 ne fonctionnera donc généralement pas. Sous Linux, l'ouverture /dev/stdout
ou la /dev/stderr
lecture (comme dans $(</dev/stdout)
) fonctionnerait et vous permettrait de lire à partir du fichier où stdout va (et si stdout était un tuyau, cela lirait à l'autre extrémité du tuyau et s'il s'agissait d'un socket , il échouerait car vous ne pouvez pas ouvrir de socket).
Dans notre cas du script exécuté sans redirection à l'invite d'un shell interactif dans un terminal, tous les fichiers / dev / stdin, / dev / stdout et / dev / stderr seront ce fichier de périphérique de terminal / dev / pts / x.
La lecture de ces fichiers spéciaux renvoie ce qui est envoyé par le terminal (ce que vous tapez sur le clavier). Leur écrire enverra le texte au terminal (pour affichage).
echo $(</dev/stdin)
echo $(</dev/stderr)
serons les mêmes. Pour développer $(</dev/stdin)
, le shell ouvrira / dev / pts / 0 et lira ce que vous tapez jusqu'à ce que vous appuyiez ^D
sur une ligne vide. Ils passeront ensuite l'expansion (ce que vous avez tapé dépouillé des sauts de ligne de fin et soumis à split + glob) echo
qui sera ensuite sorti sur stdout (pour l'affichage).
Cependant dans:
echo $(</dev/stdout)
dans bash
( et bash
seulement ), il est important de réaliser qu'à l'intérieur $(...)
, stdout a été redirigé. C'est maintenant une pipe. Dans le cas de bash
, un processus shell enfant lit le contenu du fichier (ici /dev/stdout
) et l'écrit dans le canal, tandis que le parent lit à l'autre extrémité pour compenser l'expansion.
Dans ce cas, lorsque ce processus bash enfant s'ouvre /dev/stdout
, il ouvre en fait l'extrémité de lecture du tuyau. Rien ne viendra jamais de ça, c'est une situation de blocage.
Si vous vouliez lire à partir du fichier pointé par les scripts stdout, vous devriez le contourner avec:
{ echo content of file on stdout: "$(</dev/fd/3)"; } 3<&1
Cela dupliquerait le fd 1 sur le fd 3, donc / dev / fd / 3 pointerait vers le même fichier que / dev / stdout.
Avec un script comme:
#! /bin/bash -
printf 'content of file on stdin: %s\n' "$(</dev/stdin)"
{ printf 'content of file on stdout: %s\n' "$(</dev/fd/3)"; } 3<&1
printf 'content of file on stderr: %s\n' "$(</dev/stderr)"
Lorsqu'il est exécuté en tant que:
echo bar > err
echo foo | myscript > out 2>> err
Vous verriez out
ensuite:
content of file on stdin: foo
content of file on stdout: content of file on stdin: foo
content of file on stderr: bar
Si , par opposition à la lecture de /dev/stdin
, /dev/stdout
, /dev/stderr
, vous vouliez lire stdin, stdout et stderr ( ce qui serait logique encore moins), vous feriez:
#! /bin/sh -
printf 'what I read from stdin: %s\n' "$(cat)"
{ printf 'what I read from stdout: %s\n' "$(cat <&3)"; } 3<&1
printf 'what I read from stderr: %s\n' "$(cat <&2)"
Si vous avez recommencé ce deuxième script en tant que:
echo bar > err
echo foo | myscript > out 2>> err
Vous verriez dans out
:
what I read from stdin: foo
what I read from stdout:
what I read from stderr:
et dans err
:
bar
cat: -: Bad file descriptor
cat: -: Bad file descriptor
Pour stdout et stderr, cat
échoue car les descripteurs de fichiers étaient ouverts pour l' écriture uniquement, pas pour la lecture, l'expansion de $(cat <&3)
et $(cat <&2)
est vide.
Si vous l'appeliez comme:
echo out > out
echo err > err
echo foo | myscript 1<> out 2<> err
(où <>
s'ouvre en mode lecture + écriture sans troncature), vous verriez en out
:
what I read from stdin: foo
what I read from stdout:
what I read from stderr: err
et dans err
:
err
Vous remarquerez que rien n'a été lu depuis stdout, car le précédent printf
avait écrasé le contenu de out
avec what I read from stdin: foo\n
et laissé la position stdout dans ce fichier juste après. Si vous aviez amorcé out
avec un texte plus grand, comme:
echo 'This is longer than "what I read from stdin": foo' > out
Ensuite, vous entrez out
:
what I read from stdin: foo
read from stdin": foo
what I read from stdout: read from stdin": foo
what I read from stderr: err
Voyez comment l ' $(cat <&3)
a lu ce qui restait après le premier printf
et cela a également déplacé la position stdout au-delà afin que le prochain printf
produise ce qui a été lu après.
echo x
n'est pas la même chose queecho x > /dev/stdout
si stdout ne va pas vers un canal ou certains périphériques de caractères comme un périphérique tty. Par exemple, si stdout va dans un fichier normal, le fichierecho x > /dev/stdout
sera tronqué et remplacé par son contenu aux\n
lieu d'écrirex\n
à la position courante de stdout.