/ dev/stdin、/ dev/stdout、/ dev/stderrの値を出力したい。
これが私の簡単なスクリプトです:
_#!/bin/bash
echo your stdin is : $(</dev/stdin)
echo your stdout is : $(</dev/stdout)
echo your stderr is : $(</dev/stderr)
_
私は次のパイプを使用します:
_[root@localhost home]# ls | ./myscript.sh
[root@localhost home]# testerr | ./myscript.sh
_
$(</dev/stdin)
だけが機能しているようですが、:_"${1-/dev/stdin}"
_を使用している人々が他のいくつかの質問でも見つけたが、成功せずに試してみた。
stdin
、stdout
、およびstderr
は ファイル記述子 0、1に接続されたstreamsですおよびプロセスの2。
端末または端末エミュレータのインタラクティブシェルのプロンプトでは、これらの3つのファイル記述子はすべて、端末または疑似端末デバイスファイルを開いて取得したものと同じ open file description を参照します(読み取り+書き込みモードの_/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がパイプの場合、パイプのもう一方の端から読み取り、それがソケットの場合、openソケットできないため失敗します。
ターミナルの対話型シェルのプロンプトでリダイレクトなしで実行されるスクリプトの場合、/ 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
のみ )では、$(...)
内でstdoutがリダイレクトされていることを認識することが重要です。現在はパイプです。 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
_から読み取るのではなく、stdin、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
は失敗します。これは、ファイル記述子がwriteのみで開かれていて、$(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
がout
の内容を_what I read from stdin: foo\n
_で上書きし、その直後にそのファイル内のstdoutの位置を残したため、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
の後に残ったものをどのように読み取ったかを確認し、それによってstdout位置もそれを超えて移動しましたそのため、次のprintf
は後で読み取ったものを出力します。
stdout
とstderr
は出力であり、それらから読み取ることはできず、書き込みのみが可能です。例えば:
echo "this is stdout" >/dev/stdout
echo "this is stderr" >/dev/stderr
プログラムはデフォルトでstdoutに書き込むため、最初のプログラムは次と同等です。
echo "this is stdout"
そして、あなたは他の方法でstderrをリダイレクトすることができます
echo "this is stderr" 1>&2
myscript.sh:
#!/bin/bash
filan -s
次に実行します:
$ ./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
おそらく、最初にfilan
をSudo apt install socat
でインストールする必要があります。