GNU/Linux >> Tutoriels Linux >  >> Linux

echo ou print /dev/stdin /dev/stdout /dev/stderr

stdin , stdout , et stderr sont des flux attaché 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, ces 3 descripteurs de fichiers feraient référence à la même description de fichier ouverte qui aurait été obtenue en ouvrant un terminal ou un fichier de périphérique pseudo-terminal (quelque chose comme /dev/pts/0 ) en mode lecture+écriture.

Si depuis ce shell interactif, vous démarrez votre script sans utiliser de redirection, votre script héritera de ces descripteurs de fichiers.

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 des liens symboliques spéciaux vers le fichier réel qui est ouvert sur ces descripteurs de fichier.

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 à partir de stdin signifie lire à partir du descripteur de fichier 0 (qui pointera quelque part dans le fichier référencé par /dev/stdin ).

Mais en $(</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 en lisant 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 en lecture. Ils sont censés être des flux sur lesquels vous écrivez . Ainsi, la lecture à partir du descripteur de fichier 1 ne fonctionnera généralement pas. Sous Linux, ouverture /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 tube, cela lirait à partir de l'autre extrémité du tube, et si c'était un socket, cela échouerait car vous ne pouvez pas ouvrir une prise).

Dans notre cas du script exécuté sans redirection à l'invite d'un shell interactif dans un terminal, tous /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 l'affichage).

echo $(</dev/stdin)
echo $(</dev/stderr)

serons les mêmes. Pour développer $(</dev/stdin) , le shell ouvrira ce /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é dépouillé des nouvelles lignes de fin et soumis à split + glob) à echo qui le sortira ensuite sur stdout (pour l'affichage).

Cependant dans :

echo $(</dev/stdout)

en bash (et bash uniquement), il est important de réaliser qu'à l'intérieur de $(...) , stdout a été redirigé. C'est maintenant un tuyau. Dans le cas de bash , un processus shell enfant lit le contenu du fichier (ici /dev/stdout ) et l'écrivant dans le tube, tandis que le parent lit à partir de l'autre extrémité pour créer 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 n'en sortira jamais, c'est une situation de blocage.

Si vous vouliez lire à partir du fichier pointé par les scripts stdout, vous le contourneriez 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 dans 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 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 en 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 seulement, sans lecture, l'expansion de $(cat <&3) et $(cat <&2) est vide.

Si vous l'appelez 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 en 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 a laissé la position stdout dans ce fichier juste après. Si vous aviez amorcé out avec du texte plus gros, comme :

echo 'This is longer than "what I read from stdin": foo' > out

Ensuite, vous obtiendriez 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 le $(cat <&3) a lu ce qui restait après le premier printf et cela a également déplacé la position stdout au-delà de sorte que le prochain printf 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 donc le premier est équivalent à

echo "this is stdout"

et vous pouvez rediriger stderr d'autres manières telles que

echo "this is stderr" 1>&2

Linux
  1. Linux :Différence entre /dev/console , /dev/tty et /dev/tty0 ?

  2. Quand utiliser /dev/random contre /dev/urandom ?

  3. Comment mapper les périphériques /dev/sdX et /dev/mapper/mpathY à partir du périphérique /dev/dm-Z

  4. Quand dois-je utiliser /dev/shm/ et quand dois-je utiliser /tmp/?

  5. /dev/sdb :aucun fichier ou répertoire de ce type (mais /dev/sdb1, etc. existe)

tty (/dev/tty ) vs pts (/dev/pts) sous Linux

Comment échanger /dev/sda avec /dev/sdb ?

DD de /dev/zero à /dev/null...ce qui se passe réellement

Comment Linux utilise /dev/tty et /dev/tty0

Est-ce une erreur de lier /dev/random à /dev/urandom sous Linux ?

Pourquoi < ou > sont-ils nécessaires pour utiliser /dev/tcp