web-dev-qa-db-fra.com

écho ou impression / dev / stdin / dev / stdout / dev / stderr

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.

13
Omar BISTAMI

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.

29
Stéphane Chazelas

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
2
Michael Daffin

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.

1
wisbucky