Linux の stdin、stdout、stderr とは?

  • Linux コマンドは、コマンドに関するデータを転送するために使用できる 3 つのデータ ストリーム (stdin、stdout、stderr) を作成します。
  • stdin は入力ストリーム、stdout は出力ストリーム、stderr は Linux のエラー ストリームです。
  • リダイレクトを使用すると、出力またはエラーをファイルやパイプなどの別の宛先にリダイレクトできます。

stdinstdoutstderrは、Linux コマンドを起動するときに作成される 3 つのデータ ストリームです。これらを使用して、スクリプトがパイプまたはリダイレクトされているかどうかを確認できます。その方法をご紹介します。

ストリームは 2 つのポイントを結合します

Linux や Unix 系オペレーティング システムについて学び始めるとすぐに、stdinstdoutstederrという用語に出くわすでしょう。これらは、Linux コマンドが実行されるときに確立される 3 つの標準ストリームです。コンピューティングでは、ストリームはデータを転送できるものです。これらのストリームの場合、そのデータはテキストです。

水流と同様に、データ ストリームには 2 つの端があります。ソースと流出があります。使用している Linux コマンドは、各ストリームの端を 1 つ提供します。もう一方の端は、コマンドを起動したシェルによって決定されます。その端は、コマンドを起動したコマンド ラインに従って、ターミナル ウィンドウに接続されるか、パイプに接続されるか、ファイルや他のコマンドにリダイレクトされます。

Linux 標準ストリーム: stdin、stdout、stderr

Linux では、stdinは標準入力ストリームです。これはテキストを入力として受け入れます。コマンドからのシェルへのテキスト出力は、stdout(標準出力) ストリームを介して配信されます。コマンドからのエラー メッセージは、stderr(標準エラー) ストリームを介して送信されます。

したがって、2 つの出力ストリーム (stdoutstderr) と 1 つの入力ストリーム (stdin) があることがわかります。エラー メッセージと通常の出力にはそれぞれターミナル ウィンドウにそれらを伝達するための独自の手段があるため、互いに独立して処理できます。

ストリームはファイルのように処理されます

Linux のストリームは、他のほとんどのストリームと同様に、ファイルであるかのように扱われます。ファイルからテキストを読み取ったり、ファイルにテキストを書き込んだりできます。これらのアクションの両方には、データ ストリームが関係します。したがって、データ ストリームをファイルとして処理するという概念は、それほど難しくありません。

プロセスに関連付けられた各ファイルには、それを識別するための固有の番号が割り当てられます。これはファイル記述子と呼ばれます。ファイルに対してアクションを実行する必要があるときはいつでも、ファイル記述子を使用してファイルを識別します。

これらの値は、stdinstdout,およびstderrに対して常に使用されます:

  • 0: stdin
  • 1: stdout
  • 2: stderr

パイプとリダイレクトへの対応

誰かをトピックに紹介しやすくするために、トピックの簡略化されたバージョンを教えることが一般的なテクニックです。たとえば、文法では、「C の後は I、E の前」というルールを教わります。しかし実際には、このルールには従うケースよりも例外の方が多いのです。

同様に、stdinstdoutstderrについて話すときには、プロセスは 3 つの標準ストリームがどこで終了するのかを知らないし、気にもしないという受け入れられた公理を引用するのが便利です。プロセスは、出力がターミナルに移動するかファイルにリダイレクトされるかに関係なく、気にする必要があるでしょうか? 入力が出力から来ているのか、別のプロセスからパイプで入力されているのかを判断できますか?

実際、プロセスは知っています。または少なくとも、確認することを選択した場合には見つけることができます。そして、ソフトウェアの作者がその機能を追加することを決定した場合、それに応じて動作を変更できます。

この動作の変化を簡単に確認できます。次の 2 つのコマンドを試してください。

ls

ls | cat

lsコマンドは、その出力 (stdout) が別のコマンドにパイプされている場合に、動作が異なります。1 列の出力に切り替えるのはlsであり、catによって実行される変換ではありません。また、lsの出力がリダイレクトされている場合にも同じことが行われます。

ls > capture.txt

cat capture.txt

stdout と stderr のリダイレクト

エラー メッセージを専用のストリームで配信できるという利点があります。つまり、コマンドの出力を (stdout) ファイルにリダイレクトし、ターミナル ウィンドウでエラー メッセージ (stderr) を確認できます。必要に応じて、エラーが発生したときにエラーに対応できます。また、エラー メッセージがstdoutがリダイレクトされたファイルに混入するのを防ぐこともできます。

次のテキストをエディターに入力し、error.sh というファイルに保存します。

<code>#!/bin/bash
echo "ファイルが存在しない場合にアクセスを試みる"
cat bad-filename.txt</code>

次のコマンドでスクリプトを実行可能にします。

chmod +x error.sh

スクリプトの最初の行は、stdoutストリームを介して端末ウィンドウにテキストを出力します。2 番目の行は、存在しないファイルへのアクセスを試みます。これにより、stderrを介して配信されるエラー メッセージが生成されます。

次のコマンドでスクリプトを実行します。

./error.sh

出力の両方のストリーム、stdoutstderrが端末ウィンドウに表示されていることがわかります。

出力をファイルにリダイレクトしてみましょう。

./error.sh > capture.txt

stderrを介して配信されるエラー メッセージは、引き続き端末ウィンドウに送信されます。ファイルの内容を確認して、stdout出力がファイルに送信されたかどうかを確認できます。

cat capture.txt

stdinからの出力が期待どおりファイルにリダイレクトされました。

>リダイレクト記号は、デフォルトでstdoutで機能します。数値ファイル記述子を使用して、リダイレクトする標準出力ストリームを指定できます。

stdoutを明示的にリダイレクトするには、次のリダイレクト命令を使用します。

1>

stderrを明示的にリダイレクトするには、次のリダイレクト命令を使用します。

2>

テストをもう一度試してみましょう。今回は2>を使用します。

./error.sh 2> capture.txt

エラー メッセージがリダイレクトされ、stdoutechoメッセージが端末ウィンドウに送信されます。

capture.txt ファイルの中身を見てみましょう。

cat capture.txt

stderrメッセージは、期待どおり capture.txt にあります。

stdout と stderr の両方をリダイレクトする

stdoutまたはstderrを互いに独立してファイルにリダイレクトできるのなら、2 つを同時に 2 つの異なるファイルにリダイレクトできるはずです。

はい、可能です。このコマンドはstdoutを capture.txt というファイルに、stderrを error.txt というファイルにリダイレクトします。

./error.sh 1> capture.txt 2> error.txt

出力の両方のストリーム (標準出力と標準エラー) がファイルにリダイレクトされているため、端末ウィンドウには表示される出力はありません。何も起こらなかったかのように、コマンド ライン プロンプトに戻ります。

各ファイルの内容を確認してみましょう。

cat capture.txt

cat error.txt

stdout と stderr を同じファイルにリダイレクトする

いいですね。各標準出力ストリームを専用のファイルに送信しました。できる唯一の別の組み合わせは、stdoutstderrの両方を同じファイルに送信することです。

次のコマンドでこれを実現できます。

./error.sh > capture.txt 2>&1

これを分解してみましょう。

  • ./error.sh: error.sh スクリプト ファイルを起動します。
  • > capture.txt: stdoutストリームを capture.txt ファイルにリダイレクトします。>1>の省略形です。
  • 2>&1: これは &> リダイレクト命令を使用します。この命令を使用すると、シェルに 1 つのストリームを別のストリームと同じ宛先に移動させることができます。この場合、「ストリーム 2 (stderr) を、ストリーム 1 (stdout) がリダイレクトされているのと同じ宛先にリダイレクトします」と言っています。

表示される出力はありません。これは励みになります。

capture.txt ファイルを確認して、その中に何があるかを確認してみましょう。

cat capture.txt

stdoutstderrの両方のストリームが 1 つの宛先ファイルにリダイレクトされました。

ストリームの出力をリダイレクトして破棄するには、出力を/dev/nullに送信します。

スクリプト内のリダイレクトを検出する

コマンドがストリームがリダイレクトされているかどうかを検出して、それに応じて動作を変更できる方法について説明しました。これは独自のスクリプトで実現できますか? はい、可能です。理解して使用するのは非常に簡単なテクニックです。

次のテキストをエディターに入力し、input.sh として保存します。

<code>#!/bin/bash
if [ -t 0 ]; then
echo stdin coming from keyboard
else echo stdin coming from a pipe or a file
fi</code>

次のコマンドを使用して実行可能にします。

chmod +x input.sh

重要な部分は角括弧内のテストです。-t(ターミナル) オプションは、ファイル記述子に関連付けられたファイルがターミナル ウィンドウで終了する場合に true (0) を返します。テストの引数としてファイル記述子 0 を使用しました。これはstdinを表します。

stdinがターミナル ウィンドウに接続されている場合、テストは true であることが証明されます。stdinがファイルまたはパイプに接続されている場合、テストは失敗します。

スクリプトへの入力を生成するために、任意の便利なテキスト ファイルを使用できます。ここでは dummy.txt というファイルを使用します。

./input.sh < dummy.txt

出力は、スクリプトが入力がキーボードからではなく、ファイルから来ていることを認識していることを示しています。必要に応じて、スクリプトの動作をそれに応じて変更できます。

これはファイルのリダイレクトによるものでしたが、パイプで試してみましょう。

cat dummy.txt | ./input.sh

スクリプトは、入力がパイプで入力されていることを認識します。より正確には、stdinストリームがターミナル ウィンドウに接続されていないことをもう一度認識します。

パイプもリダイレクトも使用せずにスクリプトを実行してみましょう。

./input.sh

stdinストリームはターミナル ウィンドウに接続されており、スクリプトはこれを報告します。

出力ストリームで同じことを確認するには、新しいスクリプトが必要です。次の内容をエディターに入力し、output.sh として保存します。

<code>#!/bin/bash
if [ -t 1 ]; then
echo stdout is going to the terminal window
else
echo stdout is being redirected or piped
fi</code>

次のコマンドを使用して実行可能にします。

chmod +x input.sh

このスクリプトの唯一の重要な変更点は、角括弧内のテストにあります。数字の 1 を使用して、stdoutのファイル記述子を表しています。

試してみましょう。出力をcatでパイプします。

./output | cat

スクリプトは、出力がターミナル ウィンドウに直接移動していないことを認識します。

出力をファイルにリダイレクトしてスクリプトをテストすることもできます。

./output.sh > capture.txt

ターミナル ウィンドウには出力されず、コマンド プロンプトに何も表示されません。予想どおりです。

capture.txt ファイルの中身を確認して、何がキャプチャされたかを確認できます。次のコマンドを使用して実行します。

cat capture.sh

もう一度、スクリプト内の簡単なテストによって、stdoutストリームがターミナル ウィンドウに直接送信されていないことが検出されます。

パイプやリダイレクトを使用せずにスクリプトを実行すると、stdoutがターミナル ウィンドウに直接配信されていることが検出されるはずです。

./output.sh

そして、まさにそれが表示されます。

意識の流れ

スクリプトがターミナル ウィンドウに接続されているか、パイプに接続されているか、リダイレクトされているかを判断する方法を知っていると、それに応じて動作を調整できます。

ログと診断出力を、画面に出力するかファイルに出力するかによって、詳細度を変更できます。エラー メッセージは、通常のプログラム出力とは別のファイルにログに記録できます。

よくあることですが、知識が増えるほど、選択肢も増えます。