bashでは次のことができます。
echo test >&1
(stdoutにリダイレクトしますが、すでにそこにあります)echo test >&2
(stderrにリダイレクト)echo test >&0
(stdinにリダイレクト)
最後の1つを実行してもtest
、他の2つと同じように端末が印刷されますが、その理由を理解するのは困難です。では、最初に、なぜこれがまったく機能するのですか?第二に、stdinにリダイレクトするための良い使用法はありますか?
より正確には、>&0
ファイル記述子1としてファイル記述子0を複製します。プログラムのstdinが読み取り専用に開いている場合、プログラムがstdout(ファイル記述子1)に書き込もうとすると、ファイル記述子が原因でエラーが発生します。 1も読み取り専用です。
これは、独自のファイル記述子を検査する小さなシェルスクリプトを作成することで実証できます。
10156115.sh:
#!/bin/bash
bash -c 'ls -l /proc/$$/fd' >&0
次に、識別可能なstdin、stdout、およびstderrを使用して呼び出します。
$ touch stdin
$ ./10156115.sh < stdin > stdout 2> stderr
その結果、次のようになりますstderr
。
ls: write error: Bad file descriptor
ただし、デフォルトでは、3つすべてが端末です:(出力は簡略化されています)
$ ls -l /proc/$$/fd
lrwx------ 0 -> /dev/pts/14
lrwx------ 1 -> /dev/pts/14
lrwx------ 2 -> /dev/pts/14
lrwx------ 255 -> /dev/pts/14
通常、3つすべてが実際にはオープン読み取り+書き込みであるため、>&0
通常のシェルから単独で使用した場合、リダイレクトはまったく効果がありません。
これの用途はありますか?
これの一般的な使用法はありませんが、スクリプトを呼び出した人がリダイレクトstdout
してstderr
、何らかの理由でそれを変更できない場合に、ターミナルに出力する方法を取得するためのダーティハックとして使用することができます。
if [ ! -t /dev/fd/1 -a ! -t /dev/fd/2 -a -t /dev/fd/0 ]; then
echo "My message that I really, really want to go to a terminal" >&0
fi
しかし、実際にこれを行うことはお勧めしません。
歴史的な理由から、端末の標準ファイル記述子は、読み取り専用ではなく、読み取り/書き込みで開いています(具体的には、一度開いて他のファイルにdup()
編集します)。これは、パイプ入力を取得するだけでなく、ユーザーからの入力(からstdout
、またはより一般的にはstderr
)を読み取るプログラムで役立つ場合がありますが/dev/tty
、その場合は使用の方が信頼性が高くなります。一部のシステムはこれを単なるtty以外にも適用します。*BSDは双方向にソケットペア(「パイプ」)を開き、一部のシステムユーティリティ(ufsdump
例であることを思い出します)はこれに依存しています。
ほとんどのプログラムは読み取り(または上記のように読み取り/書き込み)のために開いていることを期待しているため 、リダイレクトstdin
(つまり、書き込み専用に開いている)は一般的に有用ではありません。