データストリームのリダイレクト

LinuxBeginner
オンラインで実践に進む

はじめに

Linux コマンドは通常、terminal から入力を読み取り、結果を terminal に出力します。shell を使うと、これらのデータストリームをファイルや別のコマンドに接続できます。これをリダイレクトと呼びます。リダイレクトは、ログ記録、パイプライン、バックグラウンドジョブ、shell スクリプトの基盤です。

この実験では、標準入力、標準出力、標準エラーを扱います。> がファイルを上書きする場合と、>> が末尾に追加する場合、2>2>&1 でエラーを処理する方法、パイプでデータを渡す方法、tee で同じ出力を表示・保存する方法、/dev/null が役立つ場面を学びます。

標準入力、標準出力、標準エラーを理解する

このステップでは、小さな入力ファイルを作成し、標準入力を通してコマンドに接続します。

すべてのコマンドには、次の 3 つの標準ストリームがあります。

ディスクリプター 名前 デフォルトの接続先
0 標準入力(stdin) キーボード
1 標準出力(stdout) terminal
2 標準エラー(stderr) terminal

作業用ディレクトリを作成して移動します。

mkdir -p /home/labex/project/streams-lab
cd /home/labex/project/streams-lab

here-document を使って、3 行の入力ファイルを作成します。shell は 2 つの EOF マーカーの間にある行を cat に渡し、>cat の出力を input.txt に保存します。

cat > input.txt <<'EOF'
alpha
beta
gamma
EOF

通常、cat input.txt はファイル名を引数として受け取ります。一方、< 演算子を使うと、ファイルをディスクリプター 0、つまり標準入力に接続できます。

cat < input.txt

3 行が表示されることを確認してください。同じ入力リダイレクトを行数を数える wc -l にも使用します。

wc -l < input.txt

出力は 3 になります。ファイル名は wc の引数として渡されないため、数字だけが表示されます。

標準出力をリダイレクトして末尾に追加する

このステップでは、通常のコマンド出力をリダイレクトし、上書きと末尾への追加の違いを確認します。

stream 用の作業ディレクトリにいることを確認します。

cd /home/labex/project/streams-lab

> 演算子は、ディスクリプター 1、つまり標準出力をファイルに接続します。ファイルが存在しない場合は作成し、既存の内容がある場合は書き込み前に切り詰めます。

echo "Stream report" > report.txt
cat report.txt

もう一度上書きし、前の行が消えることを確認します。

echo "Input lines: 3" > report.txt
cat report.txt

次に、出力先を追記モードで開く >> を使用します。

echo "Status: complete" >> report.txt
cat report.txt

ファイルには、Input lines: 3Status: complete の順に、ちょうど 2 行が含まれているはずです。リダイレクトは、コマンドが開始される前に shell によって実行されます。

標準出力と標準エラーを分ける

このステップでは、通常の出力とエラーの両方を生成するコマンドを実行し、それぞれのストリームを別のファイルに保存します。

次の ls コマンドには、存在するパスと存在しないパスを 1 つずつ指定します。

cd /home/labex/project/streams-lab
ls -l input.txt missing.txt

input.txt に関する行は stdout です。No such file or directory というメッセージは stderr です。デフォルトでは両方とも terminal に表示されますが、ストリームとしては別々に扱われます。

> でディスクリプター 1 を、2> でディスクリプター 2 をリダイレクトします。

ls -l input.txt missing.txt > listing.log 2> errors.log

パスの 1 つが存在しないため、コマンドは 0 以外の終了ステータスを返します。ただし、shell は次のコマンドを入力できる状態のままです。出力先を確認します。

cat listing.log
cat errors.log

listing.log にはエラーなしで input.txt に関する情報が含まれているはずです。errors.log には missing.txt とエラーに関する情報が含まれ、正常に取得できた一覧情報は含まれていないはずです。

標準出力と標準エラーを結合する

このステップでは、stdout と stderr を 1 つのログに送り、リダイレクトの順序が重要な理由を学びます。

まず stdout を combined.log にリダイレクトし、その後、ディスクリプター 2 をディスクリプター 1 の現在の出力先に接続します。

cd /home/labex/project/streams-lab
ls -l input.txt missing.txt > combined.log 2>&1

2>&1 は、「ディスクリプター 2 を、現在ディスクリプター 1 が使用しているのと同じ出力先に送る」という意味です。> combined.log が先に記述されているため、両方のストリームがファイルに送られます。

cat combined.log

ファイルには、input.txt の一覧情報と missing.txt のエラーの両方が含まれているはずです。

Bash では、両方のストリームをリダイレクトする短縮構文として &> も使用できます。

ls -l report.txt absent.txt &> shorthand.log
cat shorthand.log

この 2 つ目のファイルには、正常に取得された report.txt の一覧情報と、absent.txt に関するエラーの両方が含まれているはずです。移植性の高い > file 2>&1 形式は、スクリプトやサービスコマンドで特によく使われます。

Tee でパイプラインの出力を表示・保存する

このステップでは、パイプラインと tee を使い、出力を表示しながら同じデータをファイルに保存します。

| で記述するパイプは、左側のコマンドの stdout を右側のコマンドの stdin に接続します。入力ファイルをアルファベットの逆順に並べ替えます。

cd /home/labex/project/streams-lab
sort -r input.txt

通常の出力リダイレクトを使うと、並べ替えた行はファイルに送られ、terminal には表示されません。

sort -r input.txt > sorted.txt
cat sorted.txt

tee コマンドは、stdin から受け取った内容を stdout と指定したファイルの両方にコピーします。同じ並べ替え処理をパイプ経由で実行します。

sort -r input.txt | tee sorted.txt

画面に並べ替えた行が表示され、sorted.txt にも同じ内容が保存されます。デフォルトでは、tee> と同じように出力先を上書きします。

-a オプションを指定すると、tee は末尾に追加します。1 行追加し、その行を表示します。

echo "delta" | tee -a sorted.txt
cat sorted.txt

ファイルには、gammabetaalphadelta の順に含まれているはずです。tee は、terminal で実行結果を確認しながら、同時にログを保存したい場合に便利です。

Dev Null で不要なデータを破棄する

このステップでは、/dev/null を使ってストリームを意図的に破棄し、tee で必要な出力を保存し、既存のログを空にします。

/dev/null は、書き込まれた内容をすべて受け取って破棄する特殊なデバイスです。実在するディレクトリと存在しないディレクトリを一覧表示し、stderr だけを破棄します。

cd /home/labex/project/streams-lab
ls -l . missing-directory 2> /dev/null

ディレクトリの一覧は表示されますが、missing-directory に関するエラーは表示されないはずです。これを tee と組み合わせ、表示される stdout も保存します。

ls -l . missing-directory 2> /dev/null | tee visible-listing.log

パイプを通るのは stdout だけです。stderr は、パイプがデータを受け取る前に /dev/null へ個別にリダイレクトされています。

保存した一覧を確認します。

cat visible-listing.log

最後に、一時的な scratch ログを作成し、/dev/null を空の入力元として使って内容を消去します。別のファイルには、ステップ 4 で結合したストリームの記録が保持されています。

echo "outdated scratch data" > scratch.log
cat /dev/null > scratch.log

サイズを確認します。

ls -l scratch.log

ファイルは存在したままで、サイズは 0 バイトになっているはずです。データの破棄は意図的に行ってください。診断情報が必要なときにエラーを隠すと、トラブルシューティングが難しくなります。

まとめ

3 つの標準ストリームとそのファイルディスクリプターを確認し、< でファイルを stdin に接続し、>>> で stdout をリダイレクトしました。また、2> で stderr を分離し、2>&1&> で stdout と結合し、演算子の順序が重要な理由を学びました。

さらに、通常のファイルリダイレクトとパイプラインおよび tee の違いを比較し、tee -a で出力を表示しながら末尾に追加し、/dev/null で不要なデータを破棄しました。これらのパターンは、このコースの後半で学ぶログ記録、バックグラウンドコマンド、shell スクリプトを理解するための基礎になります。