echo o print / dev / stdin / dev / stdout / dev / stderr

13

Quiero imprimir el valor de / dev / stdin, / dev / stdout y / dev / stderr.

Aquí está mi script simple:

#!/bin/bash
echo your stdin is : $(</dev/stdin)
echo your stdout is : $(</dev/stdout)
echo your stderr is : $(</dev/stderr)

Yo uso las siguientes tuberías:

[root@localhost home]# ls | ./myscript.sh
[root@localhost home]# testerr | ./myscript.sh

solo $(</dev/stdin)parece funcionar, también he encontrado en algunas otras preguntas que la gente usa: lo "${1-/dev/stdin}"probé sin éxito.

Omar BISTAMI
fuente

Respuestas:

29

stdin, stdouty stderrson secuencias adjuntas a los descriptores de archivo 0, 1 y 2 respectivamente de un proceso.

En el indicador de un shell interactivo en un terminal o emulador de terminal, todos esos 3 descriptores de archivo se referirían a la misma descripción de archivo abierto que se habría obtenido al abrir un archivo de dispositivo terminal o pseudo-terminal (algo así como /dev/pts/0) en lectura + escritura modo.

Si desde ese shell interactivo, inicia su secuencia de comandos sin utilizar ninguna redirección, su secuencia de comandos heredará esos descriptores de archivo.

En Linux, /dev/stdin, /dev/stdout, /dev/stderrson enlaces simbólicos a /proc/self/fd/0, /proc/self/fd/1, /proc/self/fd/2respectivamente, mismos enlaces simbólicos especiales en el archivo real que está abierto en los descriptores de fichero.

No son stdin, stdout, stderr, son archivos especiales que identifican a qué archivos van stdin, stdout, stderr (tenga en cuenta que es diferente en otros sistemas que Linux que tienen esos archivos especiales).

leer algo de stdin significa leer del descriptor de archivo 0 (que apuntará a algún lugar dentro del archivo al que hace referencia /dev/stdin).

Pero $(</dev/stdin), en el shell no se lee desde stdin, se abre un nuevo descriptor de archivo para leer en el mismo archivo que el abierto en stdin (por lo que se lee desde el inicio del archivo, no hacia dónde apunta actualmente stdin).

Excepto en el caso especial de dispositivos terminales abiertos en modo lectura + escritura, stdout y stderr generalmente no están abiertos para lectura. Están destinados a ser secuencias en las que escribes . Por lo tanto, leer del descriptor de archivo 1 generalmente no funcionará. En Linux, abrir /dev/stdouto /dev/stderrleer (como en $(</dev/stdout)) funcionaría y le permitiría leer del archivo al que va stdout (y si stdout fuera una tubería, eso leería desde el otro extremo de la tubería, y si fuera un zócalo , fallaría ya que no puede abrir un socket).

En nuestro caso, el script se ejecuta sin redireccionamiento en el indicador de un shell interactivo en una terminal, todo / dev / stdin, / dev / stdout y / dev / stderr será ese / dev / pts / x archivo de dispositivo terminal.

La lectura de esos archivos especiales devuelve lo que envía el terminal (lo que escribe en el teclado). Escribirles enviará el texto al terminal (para mostrar).

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

será lo mismo. Para expandir $(</dev/stdin), el shell abrirá ese / dev / pts / 0 y leerá lo que escribe hasta que presione ^Duna línea vacía. Luego pasarán la expansión (lo que escribió despojado de las nuevas líneas finales y sujeto a división + glob) a la echoque luego se enviará en stdout (para mostrar).

Sin embargo en:

echo $(</dev/stdout)

en bash( y bashsolo ), es importante darse cuenta de que por dentro $(...), stdout ha sido redirigido. Ahora es una pipa. En el caso de bash, un proceso de shell hijo lee el contenido del archivo (aquí /dev/stdout) y lo escribe en la tubería, mientras el padre lee desde el otro extremo para completar la expansión.

En este caso, cuando se abre ese proceso de bash secundario /dev/stdout, en realidad está abriendo el extremo de lectura de la tubería. Nada saldrá de eso, es una situación de punto muerto.

Si quisiera leer del archivo señalado por los scripts stdout, lo solucionaría con:

 { echo content of file on stdout: "$(</dev/fd/3)"; } 3<&1

Eso duplicaría el fd 1 en el fd 3, por lo que / dev / fd / 3 apuntaría al mismo archivo que / dev / stdout.

Con un guión como:

#! /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)"

Cuando se ejecuta como:

echo bar > err
echo foo | myscript > out 2>> err

Verías outdespués:

content of file on stdin: foo
content of file on stdout: content of file on stdin: foo
content of file on stderr: bar

Si en lugar de la lectura de /dev/stdin, /dev/stdout, /dev/stderr, que quería leer de la entrada estándar, stdout y stderr (lo que haría aún menos sentido), que haría:

#! /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 comenzó ese segundo script nuevamente como:

echo bar > err
echo foo | myscript > out 2>> err

Lo verías en out:

what I read from stdin: foo
what I read from stdout:
what I read from stderr:

y en err:

bar
cat: -: Bad file descriptor
cat: -: Bad file descriptor

Para stdout y stderr, catfalla porque los descriptores de archivo estaban abiertos solo para escribir , no para leer, la expansión de $(cat <&3)y $(cat <&2)está vacía.

Si lo llamaste como:

echo out > out
echo err > err
echo foo | myscript 1<> out 2<> err

(donde se <>abre en modo lectura + escritura sin truncamiento), vería en out:

what I read from stdin: foo
what I read from stdout:
what I read from stderr: err

y en err:

err

Notarás que no se leyó nada de stdout, porque el anterior printfhabía sobrescrito el contenido de outwith what I read from stdin: foo\ny había dejado la posición stdout dentro de ese archivo justo después. Si te has preparado outcon un texto más grande, como:

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

Entonces entrarías 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

Vea cómo $(cat <&3)ha leído lo que quedó después del primero printf y, al hacerlo, también movió la posición stdout para que el siguiente printfmuestre lo que se leyó después.

Stéphane Chazelas
fuente
2

stdouty stderrson salidas, no las lees, solo puedes escribirles. Por ejemplo:

echo "this is stdout" >/dev/stdout
echo "this is stderr" >/dev/stderr

los programas escriben en stdout por defecto, por lo que el primero es equivalente a

echo "this is stdout"

y puede redirigir stderr de otras maneras, como

echo "this is stderr" 1>&2
Michael Daffin
fuente
1
En Linux, echo xno es lo mismo que echo x > /dev/stdoutsi stdout no va a una tubería o algunos dispositivos de caracteres como un dispositivo tty. Por ejemplo, si stdout va a un archivo normal echo x > /dev/stdout, truncaría el archivo y reemplazaría su contenido en x\nlugar de escribir x\nen la posición stdout actual.
Stéphane Chazelas
@ Michael-Daffin> Gracias, así que si quiero leer stdout y stderr, ¿solo puedo usar cat? (ya que son archivos), por ejemplo, quiero mostrarle al usuario el último error que ocurrió echo this is your error $(cat /dev/stderr).
Omar BISTAMI
@OmarBISTAMI No puede leer stdouty stderrson salidas.
Kusalananda
1

myscript.sh:

#!/bin/bash
filan -s

Entonces corre:

$ ./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

Es probable que tenga que instalar filancon el sudo apt install socatprimero.

wisbucky
fuente