Je veux imprimer la valeur de/dev/stdin,/dev/stdout et/dev/stderr.
Voici mon script simple:
#!/bin/bash
echo your stdin is : $(</dev/stdin)
echo your stdout is : $(</dev/stdout)
echo your stderr is : $(</dev/stderr)
j'utilise les tuyaux suivants:
[root@localhost home]# ls | ./myscript.sh
[root@localhost home]# testerr | ./myscript.sh
seule $(</dev/stdin)
semble fonctionner, j'ai également trouvé sur d'autres questions des personnes utilisant: "${1-/dev/stdin}"
l'ont essayé sans succès.
stdin
, stdout
et stderr
sont streams attachés à descripteurs de fichiers 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 se réfèrent au même description du fichier ouvert qui aurait été obtenu en ouvrant un fichier de terminal ou de périphérique pseudo-terminal ( quelque chose comme /dev/pts/0
) en mode lecture + écriture.
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, eux-mêmes liens symboliques spéciaux vers le fichier réel qui est ouvert sur ces 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 où stdin actuellement pointe vers).
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 destinés à être des flux que vous écrivez à. La lecture du descripteur de fichier 1 ne fonctionnera donc généralement pas. Sous Linux, ouvrir /dev/stdout
Ou /dev/stderr
Pour la lecture (comme dans $(</dev/stdout)
) fonctionnerait et vous permettrait de lire à partir du fichier où stdout va (et si stdout était un pipe, qui se lirait de l'autre extrémité du pipe, et si c'était une socket, elle échouerait car vous ne pouvez pas ouvrir une 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 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 sur ^D
Sur une ligne vide. Ils passeront ensuite l'expansion (ce que vous avez tapé sans les sauts de ligne de fin et soumis à split + glob) à echo
qui le sortira ensuite sur stdout (pour l'affichage).
Cependant dans:
echo $(</dev/stdout)
dans bash
( et bash
uniquement ), il est important de réaliser qu'à l'intérieur de $(...)
, 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 composer le expansion.
Dans ce cas, lorsque ce processus bash enfant s'ouvre /dev/stdout
, Il ouvre en fait l'extrémité de lecture du canal. 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 verrez ensuite dans out
:
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 depuis stdin, stdout et stderr (ce qui aurait encore moins de sens), 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 écriture uniquement, pas en 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 dans 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 remplacé 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 du texte plus grand, comme:
echo 'This is longer than "what I read from stdin": foo' > out
Vous obtiendrez alors 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 la $(cat <&3)
a lu ce qui restait après le premier printf
et cela a également déplacé la position de sortie standard au-delà pour que le printf
suivant affiche ce qui a été lu après.
stdout
et stderr
sont des sorties, vous ne les lisez pas, vous pouvez seulement y écrire. Par exemple:
echo "this is stdout" >/dev/stdout
echo "this is stderr" >/dev/stderr
les programmes écrivent sur stdout par défaut, de sorte que le premier est équivalent à
echo "this is stdout"
et vous pouvez rediriger stderr par d'autres moyens tels que
echo "this is stderr" 1>&2
myscript.sh:
#!/bin/bash
filan -s
Exécutez ensuite:
$ ./myscript.sh
0 tty /dev/pts/1
1 tty /dev/pts/1
2 tty /dev/pts/1
$ ls | ./myscript.sh
0 pipe
1 tty /dev/pts/1
2 tty /dev/pts/1
$ ls | ./myscript.sh > out.txt
$ cat out.txt
0 pipe
1 file /tmp/out.txt
2 tty /dev/pts/1
Vous devrez probablement installer filan
avec Sudo apt install socat
première.