stdin, stdout, E stderrsão correntes ligados ao descritores de arquivo 0, 1 e 2, respectivamente de um processo.
No prompt de um shell interativo em um terminal ou emulador de terminal, todos esses 3 descritores de arquivo se referiam à mesma descrição de arquivo aberto que seria obtida pela abertura de um arquivo de dispositivo terminal ou pseudo-terminal (algo como /dev/pts/0) em leitura + gravação modo.
Se a partir desse shell interativo, você iniciar o script sem usar nenhum redirecionamento, o script herdará esses descritores de arquivo.
No Linux, /dev/stdin, /dev/stdout, /dev/stderrsão links simbólicos para /proc/self/fd/0, /proc/self/fd/1, /proc/self/fd/2respectivamente,-se links simbólicos especiais para o arquivo real que está aberta sobre os descritores de arquivos.
Eles não são stdin, stdout, stderr, são arquivos especiais que identificam para quais arquivos stdin, stdout, stderr vão (observe que é diferente em outros sistemas que o Linux que possuem esses arquivos especiais).
ler algo do stdin significa ler do descritor de arquivo 0 (que apontará para algum lugar do arquivo referenciado por /dev/stdin).
Mas $(</dev/stdin), em , o shell não está lendo do stdin, ele abre um novo descritor de arquivo para leitura no mesmo arquivo que o aberto no stdin (portanto, lendo desde o início do arquivo, não para onde o stdin aponta atualmente).
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 devem ser fluxos nos quais você escreve . Portanto, a leitura do descritor de arquivo 1 geralmente não funcionará. No Linux, a abertura /dev/stdoutou /dev/stderrleitura (como em $(</dev/stdout)) funcionaria e permitiria a leitura do arquivo para o qual o stdout vai (e se o stdout for um canal, ele lerá da outra extremidade do canal e se for um soquete , falharia porque você não pode abrir um soquete).
No nosso caso, o 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 arquivo de dispositivo do terminal / dev / pts / x.
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é pressionar ^Duma linha vazia. Eles passarão a expansão (o que você digitou retirou das novas linhas à direita e está sujeito a split + glob), para a echoqual a saída será exibida em stdout (para exibição).
No entanto, em:
echo $(</dev/stdout)
no bash( e bashsomente ), é importante perceber que o $(...)stdout foi redirecionado por dentro. Agora é um cano. No caso de bash, um processo de shell filho está lendo o conteúdo do arquivo (aqui /dev/stdout) e gravando no canal, enquanto o pai lê da outra extremidade para compensar a expansão.
Nesse caso, quando esse processo de bash filho é aberto /dev/stdout, ele está realmente abrindo a extremidade de leitura do pipe. Nada nunca resultará disso, é uma situação de impasse.
Se você quiser ler o arquivo apontado pelos scripts stdout, contorná-lo-á com:
{ echo content of file on stdout: "$(</dev/fd/3)"; } 3<&1
Isso duplicaria o fd 1 no fd 3, portanto / 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ê veria outdepois:
content of file on stdin: foo
content of file on stdout: content of file on stdin: foo
content of file on stderr: bar
Se em oposição à leitura de /dev/stdin, /dev/stdout, /dev/stderr, você queria ler de stdin, stdout e stderr (o que tornaria ainda 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 esse 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, catfalha porque os descritores de arquivo foram abertos apenas para gravação , não para leitura, 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ê vê em out:
what I read from stdin: foo
what I read from stdout:
what I read from stderr: err
e em err:
err
Você vai notar que nada foi lida de stdout, porque o anterior printftinha substituído o conteúdo outcom what I read from stdin: foo\ne deixou a posição stdout dentro desse arquivo logo após. Se você preparou outum texto maior, como:
echo 'This is longer than "what I read from stdin": foo' > out
Então você entra 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 $(cat <&3)ele leu o que restou após o primeiro printf e, ao fazê-lo, também moveu a posição stdout além dela, para que as próximas printfsaídas produzam o que foi lido depois.
echo xnão é o mesmo queecho x > /dev/stdoutse o stdout não vá para um pipe ou para alguns dispositivos de caracteres como um dispositivo tty. Por exemplo, se stdout for para um arquivo normalecho x > /dev/stdout, truncará o arquivo e substituirá seu conteúdo por, emx\nvez de escrever,x\nna posição atual de stdout.