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

3

Eu quero imprimir o valor de / dev / stdin, / dev / stdout e / dev / stderr.

Aqui está o meu script simples:

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

eu uso os seguintes canais:

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

Apenas $(</dev/stdin) parece funcionar, eu também encontrei em algumas outras perguntas pessoas usando: "${1-/dev/stdin}" tentou sem sucesso.

    
por Omar BISTAMI 27.10.2017 / 15:41

3 respostas

11

stdin , stdout e stderr são streams anexados a descritores de arquivos 0, 1 e 2 respectivamente de um processo.

No prompt de um shell interativo em um terminal ou emulador de terminal, todos esses três descritores de arquivo se refeririam ao mesmo descrição do arquivo aberto que teria sido obtida abrindo um arquivo de dispositivo terminal ou pseudo-terminal (algo como /dev/pts/0 ) no modo leitura + gravação.

Se a partir desse shell interativo você iniciar o script sem usar nenhum redirecionamento, o script herdará esses descritores de arquivos.

No Linux, /dev/stdin , /dev/stdout , /dev/stderr são links simbólicos para /proc/self/fd/0 , /proc/self/fd/1 , /proc/self/fd/2 , respectivamente, eles próprios links simbólicos para o arquivo real que está aberto nesses descritores de arquivo. / p>

Eles não são stdin, stdout, stderr, são arquivos especiais que identificam quais arquivos stdin, stdout, stderr vão (note que é diferente em outros sistemas que não o Linux que possuem esses arquivos especiais).

ler algo de stdin significa ler o descritor de arquivo 0 (que apontará para algum lugar dentro do arquivo referenciado por /dev/stdin ).

Mas em $(</dev/stdin) , o shell não está lendo stdin, ele abre um novo descritor de arquivo para leitura no mesmo arquivo que o aberto em stdin (portanto, lendo desde o início do arquivo, não onde stdin atualmente aponta para).

Exceto no caso especial de dispositivos de terminal abertos no modo de leitura + gravação, stdout e stderr geralmente não estão abertos para leitura. Eles são feitos para serem fluxos que você escreve para . Portanto, a leitura do descritor de arquivo 1 geralmente não funcionará. No Linux, abrir /dev/stdout ou /dev/stderr para leitura (como em $(</dev/stdout) ) funcionaria e permitiria que você lesse o arquivo para o qual o stdout acessa (e se stdout fosse um canal, ele seria lido do outro lado o pipe, e se fosse um socket, ele falharia, pois você não pode abrir um soquete).

Em nosso caso do script executado sem redirecionamento no prompt de um shell interativo em um terminal, todos os / dev / stdin, / dev / stdout e / dev / stderr serão o dispositivo de terminal / dev / pts / x arquivo.

A leitura desses arquivos especiais retorna o que é enviado pelo terminal (o que você digita no teclado). Escrever para eles enviará o texto para o terminal (para exibição).

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

será o mesmo. Para expandir $(</dev/stdin) , o shell abrirá / dev / pts / 0 e lerá o que você digitar até que você pressione ^D em uma linha vazia. Eles então passarão a expansão (o que você digitou despojado das novas linhas à direita e sujeito a split + glob) para echo , que irá então mostrar no stdout (para exibição).

No entanto, em:

echo $(</dev/stdout)

em bash ( e bash only ), é importante perceber que dentro de $(...) , o stdout foi redirecionado. Agora é um tubo. No caso de bash , um processo de shell filho está lendo o conteúdo do arquivo (aqui /dev/stdout ) e gravando-o no canal, enquanto o pai lê do outro lado para compor a expansão.

Nesse caso, quando esse processo bash filho abre /dev/stdout , ele está abrindo a extremidade de leitura do canal. Nada virá disso, é uma situação de impasse.

Se você quisesse ler o arquivo apontado pelo script stdout, você contornaria com:

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

Isso duplicaria o fd 1 no fd 3, então / dev / fd / 3 apontaria para o mesmo arquivo que / dev / stdout.

Com um script 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)"

Quando executado como:

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

Você verá em out depois:

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

Se ao contrário de ler /dev/stdin , /dev/stdout , /dev/stderr , você quisesse ler stdin, stdout e stderr (o que faria menos sentido), você faria:

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

Se você iniciou o segundo script novamente como:

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

Você veria em out :

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

e em err :

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

Para stdout e stderr, cat falha porque os descritores de arquivos estavam abertos para somente , não lendo, a expansão de $(cat <&3) e $(cat <&2) está vazia.

Se você o chamou como:

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

(onde <> é aberto no modo de leitura + gravação sem truncamento), você verá em out :

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

e em err :

err

Você notará que nada foi lido no stdout, porque o printf anterior substituiu o conteúdo de out por what I read from stdin: foo\n e deixou a posição stdout dentro desse arquivo logo após. Se você tivesse preparado out com algum texto maior, como:

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

Então você entraria em 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

Veja como o $(cat <&3) leu o que sobrou após o primeiro printf e isso também moveu a posição stdout além dele para que o próximo printf mostre o que foi lido depois.

    
por 27.10.2017 / 16:24
1

stdout e stderr são saídas, você não lê a partir delas você só pode escrever para elas. Por exemplo:

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

programas escrevem para stdout por padrão, então o primeiro é equivalente a

echo "this is stdout"

e você pode redirecionar o stderr de outras maneiras, como

echo "this is stderr" 1>&2
    
por 27.10.2017 / 15:49
1

myscript.sh:

#!/bin/bash
filan -s

Em seguida, execute:

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

Você provavelmente precisará instalar filan com sudo apt install socat primeiro.

    
por 02.11.2018 / 09:28