コマンドラインインターフェースの世界では、プログラムは次の3つの標準データストリームとやり取りすることがよくあります:
stdin):プログラムが入力を受け取る場所です(通常はキーボードから)。stdout):プログラムが通常の出力を送る場所です(通常はターミナル画面へ)。stderr):プログラムがエラーメッセージを送る場所です(通常はターミナル画面へ)。これらはパイプラインとコマンドリストの章で紹介したことを思い出してください。
パイプラインはリダイレクトの一種と考えることができます。あるプログラムの標準出力が、別のプログラムの標準入力へ直接送られるからです。 パイプラインについてはすでに説明しました。 このコンセプトでは、ファイルへのリダイレクトとファイルからのリダイレクトを扱います。
シェルのリダイレクトは、これらのストリームの送り先を変えられる強力な仕組みです。 プログラムがキーボードから読み込んだり画面に書き込んだりする代わりに、ファイルから入力をリダイレクトしたり、出力をファイルへ送ったりできます。 これにより、プログラムがデータとやり取りする方法を、柔軟に、細かく制御できます。
よく使うリダイレクト演算子と、それぞれの働きを紹介します。
>(出力のリダイレクト)目的:コマンドの標準出力(stdout)をファイルへリダイレクトします。
動作:
例:
# Sends the output of 'ls -l' to 'file_listing.txt'
ls -l > file_listing.txt
リダイレクトは、コマンドが実行される前に行われます。 つまり、読み込みたいファイルと同じファイルへリダイレクトすることはできません。
date > date.txt # populate the file with the current date
cat date.txt > date.txt # 'date.txt' is now empty!
上の例の2つ目のコマンドでは、次のことが起こります。
>>(出力の追記リダイレクト)目的:コマンドの標準出力(stdout)をファイルへ追記します。
動作:
例:
# Adds "Another line" to the end of the file
echo "Another line" >> file_listing.txt
<(入力のリダイレクト)目的:コマンドの標準入力(stdin)をファイルからリダイレクトします。
動作:コマンドは、キーボードではなく指定されたファイルから入力を受け取ります。
例:
# 'tr' reads from 'lowercase.txt' and
# output is redirected to 'uppercase.txt'
tr 'a-z' 'A-Z' < lowercase.txt > uppercase.txt
2>(エラーのリダイレクト):stderr)をファイルへリダイレクトします。# Error message from 'rm' is sent to 'error_log.txt'
rm non_existent_file 2> error_log.txt
2>>を使えば、標準エラー出力をファイルへ_追記_することもできます。
stdinとstderrの両方をまとめるリダイレクトもあります。
詳しくはマニュアルを参照してください。
/dev/null/dev/nullは特別なファイルで、次のような性質があります。
「ビットバケット」と呼ばれることもあります。 出力やエラーを抑えるのに便利です。
some_command > /dev/null # Discards stdout
some_command 2> /dev/null # Discards stderr
# Using /dev/null as input will prevent the command
# from reading data from the keyboard
some_command < /dev/null
複数のリダイレクトを一度に指定できます。
some_command < input.txt > output.txt 2> error.txt
リダイレクトはコピーできます。
# send stderr to the same destination as stdout
some_command > output.txt 2>&1
2>&1を見かけたら、「stderrを、stdoutが現在指している場所へリダイレクトする」と読んでください。
Bashはリダイレクトの指示を厳密に左から右へ処理します。つまり、次のようなことができます。
# redirect stdout to 'output1.txt'
# redirect stderr to the same place
# then change stdout to 'output2.txt'
some_command > output1.txt 2>&1 > output2.txt
シェルプログラミングやLinux/Unixのようなオペレーティングシステムの文脈では、ファイルディスクリプター(FD)は、開かれたファイルやI/O(入出力)リソースを識別・操作するための非負の整数です。 オペレーティングシステムが、開いているファイルやデータストリームを管理するために使う、ラベルや参照番号のようなものだと考えてください。
リダイレクトの左側に書かれている数字が、ファイルディスクリプターです。
標準のI/Oストリームには、最初の3つのファイルディスクリプターが割り当てられています。
新しいファイルディスクリプターは、execコマンドとリダイレクト演算子を使って設定・作成できます。
# create FD 3 as a copy of stdout
exec 3>&1
# send stdout to a log file
exec > log.txt
# then redirect both stdout and file descriptor 3
echo "some debug message" >&3 # displayed in the terminal
some_command 2>&1 # stdout and stderr send to 'log.txt'
このテクニックはかなり入り組んで見えますが、スクリプトがほかのソフトウェアから実行され、標準出力がそのソフトウェアに自動的に取り込まれるような状況では、とても役立ちます。
このトラックの単体テストを実行するために使われるbatsコマンドは、まさにこのように動作します。batsはテストの標準出力と標準エラー出力を取り込みますが、FD 3へ送られたデバッグメッセージはターミナルに表示されます。
リダイレクトはなぜ役立つのでしょうか?
シェルのリダイレクトを使えば、プログラムがデータとやり取りする方法を細かく制御でき、複雑なワークフローを組み立てたり、情報を効率よく管理したりできます。