\r\n\r\n
stdin、stdout、stderrは、Linuxのコマンドを起動したときに作成される3つのデータストリームです。これらを使って、スクリプトがパイプで送られているのか、リダイレクトされているのかを判断することができます。その方法を紹介します。
LinuxやUnix系OSを学び始めると、stdin, stdout, stderrという用語に出会うことになります。Linuxのコマンドを実行する際に作成される標準的なストリームはこの3つである。コンピューティングでは、ストリームとは、データを転送するためのものである。これらのストリームの場合、そのデータはテキストである。
データの流れには、水の流れと同じように両端があります。源流と流出があるのです。どのLinuxコマンドを使っても、それぞれのストリームの片方を提供します。もう一方の端は、startコマンドのシェルで決定されます。開始コマンドのコマンドラインに応じて、そのエンドは、ターミナルウィンドウに接続したり、パイプに接続したり、ファイルや他のコマンドにリダイレクトしたりします。
Linuxでは、stdinは標準入力ストリームです。テキストを入力として受け付ける。コマンドからシェルに出力されるテキストは、stdout(標準出力)ストリームを経由して渡されます。コマンドのエラーメッセージは、stderr(標準エラー)ストリームを介して送信されます。
つまり、stdoutとstderrという2つの出力ストリームと、stdinという1つの入力ストリームがあることがわかります。 エラーメッセージと通常の出力は、それぞれ端末ウィンドウに転送するパイプラインを持っているので、それぞれ独立して処理することができるのです。
Linuxのストリームは、他のほとんどと同様に、ファイルのように扱われます。ファイルからテキストを読み取ったり、ファイルにテキストを書き込むことができます。これらの動作は、いずれもデータストリームを伴うものです。したがって、データストリームをファイルとして扱うという考え方は、それほど単純なものではありません。
プロセスに関連する各ファイルには、それを識別するための一意の番号が割り当てられています。これをファイルディスクリプタと呼びます。ファイルディスクリプタは、ファイルに対する操作が必要なときに、ファイルを識別するために使用されます。
stdin, stdout, stderrは常にこれらの値が使用されます。
あるトピックを誰かに紹介するとき、そのトピックを簡略化して教えるのが一般的な手法です。例えば、文法については、「I before E, except after C」というルールがあると言われています。しかし、現実には、この法則が守られているケースよりも、例外の方が多いのです。
同様に、stdin, stdout, stderrについて議論する場合、プロセスはその3つの標準ストリームがどこで終了するかは知らないし気にしないという公理を提示すると便利である。プロセスは、その出力が端末に送られるか、ファイルにリダイレクトされるかを気にする必要があるのでしょうか?キーボードからの入力なのか、他のプロセスから渡されたものなのか、判別できるのでしょうか?
実際には、プロセスは確認することを選択すれば、少なくとも知ることができるし、もしソフトウェアの作者がこの機能を追加することを決定すれば、それに応じて動作を変更することができる。
この動作の変化は簡単に確認できます。以下の2つのコマンドを試してみてください。
ls ls | catlsコマンドの出力(stdout)が他のコマンドにパイプされている場合、lsコマンドの動作が異なります。1列の出力への切り替えはlsで行い、catで行う変換は行わない。lsの出力がリダイレクトされた場合、同じ動作をします。
ls > capture.txt cat capture.txtエラーメッセージを専用のストリームで配信することにはメリットがあります。つまり、コマンドの出力(stdout)をファイルにリダイレクトしても、端末のウィンドウ(stderr)にエラーメッセージが表示されるのです。必要であれば、エラーが発生したときに対応することも可能です。また、エラーメッセージが stdout がリダイレクトされるファイルを汚染するのを防ぐことができます。
以下のテキストをエディタで入力し、error.shというファイルに保存してください。
#!/bin/bash echo "About to try to access a file that doesn't exist" cat bad-filename.txt以下のコマンドで、スクリプトを実行可能な状態にします。
chmod +x error.shスクリプトの最初の行は、stdoutストリームを介してターミナルウィンドウにテキストを表示し直します。2行目は存在しないファイルにアクセスを試みています。この場合、標準エラーメッセージが出力されます。
以下のコマンドでスクリプトを実行します。
./error.sh出力ストリーム stdout と stderr の両方がターミナルウィンドウに表示されているのが見えます。
出力をファイルにリダイレクトしてみましょう。
./error.sh > capture.txtstderrで渡されたエラーメッセージは、そのまま端末ウィンドウに送られます。stdoutの出力がそのファイルに行くかどうか、ファイルの中身を確認することができます。
cat capture.txtstdinの出力は期待通りにファイルにリダイレクトされます。
デフォルトでは、> リダイレクトシンボルは標準出力で機能します。数値ファイルディスクリプタの1つは、リダイレクトされる標準出力ストリームを示すために使用することができる。
stdoutを明示的にリダイレクトするには、以下のredirectディレクティブを使用します。
1>stderrを明示的にリダイレクトするには、次のredirectコマンドを使用します。
2>もう一度、今度は2>を使ってテストしてみましょう。
./error.sh 2> capture.txtエラーメッセージはリダイレクトされ、標準出力のリターンメッセージは、ターミナルウィンドウの
capture.txtの中身を見てみましょう。
cat capture.txtstderrメッセージは、予想通りcapture.txtにあります。
確かに、stdoutやstderrをそれぞれ別のファイルにリダイレクトできるなら、同時に2つのファイルにリダイレクトできるはずですよね。
このコマンドは、標準出力にcapture.txtというファイルとerror tableというファイルを指し示します。
./error.sh 1> capture.txt 2> error.txt出力ストリーム(標準出力と標準エラー)はファイルにリダイレクトされるため、ターミナルウィンドウに表示される出力はありません。何事もなかったかのように、コマンドラインプロンプトに戻ります。
では、それぞれの資料の内容をみてみよう。
cat capture.txt cat error.txt大丈夫です、標準出力のストリームはそれぞれ専用のファイルに入れました。他にできる組み合わせは、stdoutとstderrを同じファイルに送ることくらいです。
次のコマンドで実行できます。
./error.sh > capture.txt 2>&1少し分解してみましょう。
目に見える出力はありませんが、これは励みになります。
capture.txtのアーカイブの中身を調べてみましょう。
cat capture.txtstdoutとstderrの両方のストリームは、単一の宛先ファイルにリダイレクトされました。
ストリームの出力をリダイレクトして静かに破棄するには、出力を /dev/null に向けます。
コマンドがどのようにストリームがリダイレクトされたかどうかを検出し、それに応じて動作を変更することを選択する方法について説明しました。自作のスクリプトでできるのか?はい、できます。わかりやすく、応用しやすい仕掛けになっています。
以下のテキストをエディターに入力し、input.shとして保存してください。
#!/bin/bash if [ -t 0 ]; then echo stdin coming from keyboard else echo stdin coming from a pipe or a file fi以下のコマンドで実行可能な状態にします。
chmod +x input.shスマートなのは、角括弧の中のテストです。t(端末)オプションは、ファイルディスクリプタに関連するファイルが端末ウィンドウで終了する場合、真(0)を返す。テストの引数として、stdinを表すファイルディスクリプタ0を使用する。
stdinがターミナルウィンドウに接続されている場合、テストは正しいことが証明されます。stdinがファイルまたはパイプに接続されている場合、テストは失敗します。
スクリプトの入力を生成するために、任意の便利なテキストファイルを使用することができます。ここでは、virtual.txtというファイルを使用しています。
./input.sh < dummy.txtこの出力は、入力がキーボードからではなく、ファイルからであることをスクリプトが認識していることを示しており、もしそうすることを選択すれば、それに応じてスクリプトの動作を変更することができます。
これはファイルのリダイレクトですね、パイプで試してみましょう。
cat dummy.txt | ./input.shスクリプトは入力がパイプで送られてきていることを認識する。より正確には、stdinストリームがターミナルウィンドウに接続されていないことを再度認識するのである。
パイプもリダイレクトも使わないで、スクリプトを実行してみましょう。
./input.shstdinストリームはターミナルウィンドウに接続され、スクリプトはこれを適宜報告します。
出力ストリームで同じことを確認するには、新しいスクリプトが必要です。エディタに次のように入力し、output.shとして保存してください。
#!/bin/bash if [ -t 1 ]; then echo stdout is going to the terminal window else echo stdout is being redirected or piped fi以下のコマンドで実行可能な状態にします。
chmod +x input.shこのスクリプトの唯一の大きな変更点は、角括弧内のテストである。ここでは、stdoutのファイルディスクリプタを示すために1という数字を使用している。
試しに、出力をcatで転送してみましょう。
./output | catスクリプトは、その出力が直接ターミナルウィンドウに送られないことを認識します。
また、出力をファイルにリダイレクトすることで、スクリプトをテストすることもできます。
./output.sh > capture.txtターミナルウィンドウには何も出力されないので、黙ってコマンドプロンプトに戻ります。
capture.txtの中を見れば、何がキャプチャされているかがわかる。これを行うには、次のコマンドを使う。
cat capture.shここでも、スクリプトの簡単なテストで、stdoutストリームがターミナルウィンドウに直接送信されていないことを検出します。
パイプやリダイレクトを使わずにスクリプトを実行すると、stdoutが直接ターミナルウィンドウに渡されることが検出されるはずです。
./output.shこれはまさに、私たちの目に映るものです。
スクリプトが端末ウィンドウに接続されているか、パイプに接続されているか、リダイレクトされているかを見分ける方法を知っていれば、それに応じてスクリプトの動作を調整することができます。
ログと診断出力は、画面に出力するかファイルに出力するかによって、より詳細なものにすることも、そうでないものにすることもできます。エラーメッセージは、通常のプログラム出力とは別のファイルにログ出力することができます。
原則的に、知識が増えれば選択肢が増える。