Linux でのリダイレクト (標準出力と標準エラー出力) と “2>&1 >” について理解する


Linux でのリダイレクトについて (標準出力と標準エラー出力)


Linux でコマンドを打っているとコマンドの実行結果は画面に表示され、またコマンドでエラーがあってもエラーメッセージが画面に表示されます。

ですが実際には Linux での画面出力は標準出力と標準エラー出力に分かれており、それぞれの出力(通常の画面出力とエラー出力)を別々に取り扱うことが出来ます。





以下は "hostname" コマンドでホスト名を表示するコマンドの出力と、"hostnama" というコマンドをわざと打ち間違えてエラーメッセージを表示しています。どちらも画面には表示されていますが、これらの通常出力とエラー出力を別々に取り扱う方法を試してみます。

$ hostname
hogehoge

$ hostnama
zsh: command not found: hostnama


なおリダイレクトは、ある出力の出力先を別のものに変更すると言う意味で、以下は hostname コマンドの結果を画面ではなく hoge.log というファイルに出力するよう切り替える、と言う意味になります。
$ hostname > hoge.log


標準出力と標準エラー出力の違い

まず標準出力は "1" で表されるもので、標準出力は "2" で表されています。なお標準出力の "1" は省略可能で、リダイレクト記号の ">" は "1>" と等価です。

では理論より先に標準出力と標準エラー出力の違いを知るために、早速実際にシェルスクリプトを作成して試してみます。これは単純に "Hello World." という文字列を出力しているだけです。

#!/bin/bash

echo "Hello World."

そしてシェルスクリプトに実行権を与えておきましょう。
$ chmod 744 output.sh

このスクリプトを実行すると画面に "Hello World." と表示されますね。この出力は標準出力に書き出されており、そのまま画面に表示されています。

$ ./output.sh 
Hello World.


では標準出力は "1" で表されますので("1" は省略可能)、ファイルに内容をリダイレクトしてみます。"Hello World." が画面に出力されずに、シェルスクリプトの内容がファイルに書き出されているのが分かります。
$ ./output.sh 1> output.log    (標準出力を output.log に出力している)
$ ./output.sh > output2.log    (標準出力を output.log に出力している)

$ cat output.log    (output.log に出力内容が書かれている)
Hello World.

$ cat output2.log    (output2.log に出力内容が書かれている)
Hello World.


では今度は標準エラー出力は "2" で表されますので、"2>" で標準エラー出力の内容をファイルに書き出してみます。
$ ./output.sh 2> error.log    (標準エラー出力を error.log に出力している)
Hello World.

$ cat error.log    (error.log には何も書かれていない)

しかしシェルスクリプトは "Hello World." という文字列を標準出力("1")に出力していますので、標準エラー出力には何も出力されていませんのでファイルの内容は空っぽです。

では今度はシェルスクリプトの内容を書き換えて、わざとエラーが出るようにします。"echo" コマンドが正しいですが、エラーを出すために "eeeecho" という存在しないコマンドに書き換えてあります。
#!/bin/bash

eeeecho "Hello World."


このシェルスクリプトを実行すると、以下のようにエラーが画面に表示されます。つまりエラーメッセージは標準エラー出力に表示されますが、Linux ターミナル上では画面に表示されます。
$ ./output.sh 
./output.sh: line 3: eeeecho: コマンドが見つかりません


では再度シェルスクリプトを実行して、標準出力("1")をファイルに書き出してみます。
$ ./output.sh 1> output.log
./output.sh: line 3: eeeecho: コマンドが見つかりません

$ cat output.log    (ファイルには何も書かれていない)


$ ./output.sh > output2.log
./output.sh: line 3: eeeecho: コマンドが見つかりません

$ cat output.log    (ファイルには何も書かれていない)

エラーメッセージは標準エラー出力に向かって出力されていますので、当然標準出力には何も出力されていませんのでリダイレクトしても何もファイルには書き出されていません。

今度はシェルスクリプトを実行して、標準エラー出力("2")をファイルに書き出してみます。
$ ./output.sh 2> error.log

$ cat error.log 
./output.sh: line 3: eeeecho: コマンドが見つかりません

シェルスクリプトを実行しても何も画面には表示されず、今度は標準エラー出力に出力されたメッセージがファイルに書き出されているのがわかります。

このように標準出力と標準エラー出力は全く別々に扱うことが出来る、ということです。


cron ジョブの出力を漏れ無くファイルに記録する

ここまでの情報で標準出力と標準エラー出力は別々であるということが理解出来ましたので、今度は cron ジョブについて考えてみます。

cron ジョブは指定した時間で実行されるジョブですが、当然ながら結果を出力する画面もなければエラーメッセージを表示する画面も存在しません。従って cron ジョブを実行した結果を確認するには結果をファイルにリダイレクトする必要があります。

以下は毎分実行される hoge.sh というシェルスクリプトを cron に登録し、その出力をファイルに書き出している設定です。
* * * * * /tmp/hoge.sh > /tmp/hoge.log


しかし先に述べたように上記は標準出力のみを hoge.log ファイルに出力していますが、何らかのエラーが発生した場合には hoge.log ファイルには何も記録されず、エラーメッセージもファイルに書き出されません。

ですのでやるべきことは標準出力も標準エラー出力も両方をファイルに記録すべきです。それによって成功時も失敗時も情報が全てファイルに記録されます。
ということで前述の cron 設定は次のようにすると全てがファイルに出力されます。
* * * * * /tmp/hoge.sh > /tmp/hoge.log 2>&1

ここで "> /tmp/hoge.log" は標準出力を hoge.log ファイルに書き出すということがわかりますが、"2>&1" とはなんでしょうか。

"2>&1" を分解して考えると、"2>" は標準エラー出力を他のものにリダイレクト(出力先変更)するということで、"&1" は直前の出力を "1" に繋ぎ替えるという意味になります。つまり標準エラー出力を標準出力に切り替える、ということですね。

そうすると、既に標準出力は hoge.log ファイルにリダイレクト指示をしていますので、結果的に標準エラー出力も標準エラー出力に切り替えられて、全てがファイルに書き出されるということになります。

cron ジョブでは常に "> [ファイル名] 2>&1" としておけば、漏れ無く出力をファイルに記録することが出来ますので、必ず設定するようにしましょう。