stdin
、stdout
、およびstderr
であるストリームに添付ファイル記述子 0、1、及び2は、それぞれ、プロセスの。
端末または端末エミュレータのインタラクティブシェルのプロンプトで、これらの3つのファイル記述子はすべて、読み取りまたは書き込みで端末または疑似端末デバイスファイル(のようなもの)を開いて取得したものと同じ開いているファイルの説明を参照します。/dev/pts/0
モード。
その対話型シェルからリダイレクトを使用せずにスクリプトを開始すると、スクリプトはそれらのファイル記述子を継承します。
Linuxでは、/dev/stdin
、/dev/stdout
、/dev/stderr
へのシンボリックリンクです/proc/self/fd/0
、/proc/self/fd/1
、/proc/self/fd/2
それらのファイルディスクリプタで開かれている実際のファイルへの自身の特別なシンボリックリンク、それぞれ。
それらはstdin、stdout、stderrではなく、stdin、stdout、stderrが移動するファイルを識別する特別なファイルです(これらの特別なファイルを持つLinux以外のシステムでは異なることに注意してください)。
stdinから何かを読み取るとは、ファイル記述子0(によって参照されるファイル内のどこかを指す)から読み取ることを意味します/dev/stdin
。
しかし$(</dev/stdin)
、シェルではstdinからの読み取りではなく、stdinで開いているファイルと同じファイルを読み取るために新しいファイル記述子を開きます(つまり、現在stdinが指している場所ではなく、ファイルの先頭から読み取ります)。
端末デバイスが読み取り+書き込みモードで開かれている特別な場合を除いて、stdoutとstderrは通常、読み取り用に開かれていません。それらは、あなたが書き込むストリームであることを意図しています。したがって、ファイル記述子1からの読み取りは、通常は機能しません。Linuxでは、(/dev/stdout
またはの/dev/stderr
ように$(</dev/stdout)
)を開いたり、読み取ったりすると機能し、stdoutの移動先のファイルから読み取ることができます(stdoutがパイプの場合は、パイプの反対側から読み取られ、ソケットの場合) 、ソケットを開けないため失敗します)。
ターミナルのインタラクティブシェルのプロンプトでリダイレクトなしで実行されるスクリプトの場合、/ dev / stdin、/ dev / stdout、/ dev / stderrはすべて/ dev / pts / xターミナルデバイスファイルになります。
これらの特殊ファイルから読み取ると、端末から送信されたもの(キーボードで入力したもの)が返されます。それらに書き込むと、端末にテキストが送信されます(表示用)。
echo $(</dev/stdin)
echo $(</dev/stderr)
同じになります。を展開するため$(</dev/stdin)
に、シェルはその/ dev / pts / 0を開き^D
、空の行を押すまで入力内容を読み取ります。その後、展開(入力した末尾の改行を取り除き、split + globの対象)を渡し、echo
stdout(表示用)に出力します。
ただし:
echo $(</dev/stdout)
中にbash
(とbash
のみ)、その内部を認識することが重要だ$(...)
、標準出力にリダイレクトされています。現在はパイプです。の場合bash
、子シェルプロセスはファイルの内容(ここでは/dev/stdout
)を読み取り、パイプに書き込みますが、親は反対側から読み取り、拡張を構成します。
この場合、その子bashプロセスが開くと/dev/stdout
、実際にはパイプの読み取り側が開かれます。それからは何も起こりません、それはデッドロックの状況です。
スクリプトstdoutが指すファイルから読み取りたい場合は、次のようにして回避します。
{ echo content of file on stdout: "$(</dev/fd/3)"; } 3<&1
これはfd 1をfd 3に複製するため、/ dev / fd / 3は/ dev / stdoutと同じファイルを指します。
次のようなスクリプトで:
#! /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)"
次のように実行すると:
echo bar > err
echo foo | myscript > out 2>> err
あなたはout
後で見るでしょう:
content of file on stdin: foo
content of file on stdout: content of file on stdin: foo
content of file on stderr: bar
もしからの読み取りとは反対に/dev/stdin
、/dev/stdout
、/dev/stderr
、あなたは標準入力から読みたかった、stdoutとstderr(でもあまり理にかなっている)、あなたは何だろう。
#! /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)"
次のように2番目のスクリプトを再度開始した場合:
echo bar > err
echo foo | myscript > out 2>> err
あなたが見るでしょうout
:
what I read from stdin: foo
what I read from stdout:
what I read from stderr:
そしてでerr
:
bar
cat: -: Bad file descriptor
cat: -: Bad file descriptor
stdoutとstderrの場合は、cat
ファイルディスクリプタがために開いていたので失敗した書き込みを、読んでいない、の拡張のみ$(cat <&3)
と$(cat <&2)
空です。
あなたがそれを次のように呼んだ場合:
echo out > out
echo err > err
echo foo | myscript 1<> out 2<> err
(ここで<>
、切り捨てなしで読み取り+書き込みモードで開きます)、次のように表示されout
ます:
what I read from stdin: foo
what I read from stdout:
what I read from stderr: err
そしてでerr
:
err
以前printf
はstdoutから何も読み取られなかったことに気づくでしょう。なぜなら、以前はout
with の内容を上書きし、what I read from stdin: foo\n
その直後にそのファイル内のstdout位置を残したからです。次のout
ように、より大きなテキストで準備した場合:
echo 'This is longer than "what I read from stdin": foo' > out
それからあなたは入るでしょう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
どのように参照してください$(cat <&3)
最初の後に残されたものを読んでいるprintf
と、そうもそれを過ぎて標準出力位置を移動するので、次のことをprintf
出力が後に読んでいたもの。
echo x
と同じではありecho x > /dev/stdout
ません。たとえば、stdoutが通常のファイルに移動echo x > /dev/stdout
すると、現在のstdout位置x\n
に書き込む代わりに、ファイルが切り捨てられ、その内容が置き換えられますx\n
。