質問
-
command > dev/null 2>&1
ってよく書いてませんか? - crontabでよく書いてませんか?
- でもそれ、前のcrontabコピペして書いてませんか?
- 実は意味を理解せずに書いてませんか?
ゴール
- ファイルディスクリプタを理解する
-
command > /dev/null 2>&1
の意味をきちんと理解する
基礎知識
1. ファイルディスクリプタ
Unixには、次の3つの入出力があり、それぞれ番号が振られています。
- 0: 標準入力
- 1: 標準出力
- 2: 標準エラー出力
ファイルディスクリプタとは、これら入出力をOSが判別する為に割り当てられた番号の事です。
2. リダイレクト
よく>
という記号を見ると思いますが、これがリダイレクトです。
例えば、標準出力に出すだけのシェルスクリプトを作ってみます。
#/bin/bash
echo "これは標準出力です"
普通に実行すれば標準出力に表示されますが、リダイレクトさせると、別の場所に出力する事が出来るようになります。
ファイルにリダイレクトする場合。
$ ./echo.sh > result.txt
$ cat result.txt
これは標準出力です
3. 特定のファイルディスクリプタにリダイレクトさせる
標準エラー出力にリダイレクトさせる場合はこのようにします。
$ ./echo.sh >&2
これは標準出力です
逆に標準出力にリダイレクトさせる場合はこうです。
$ ./echo.sh >&1
これは標準出力です
今は意味が分からなくてもOKです。
あとで出てます。
4. 出力結果を捨てる
/dev/null
というのはunixのスペシャルファイルの事で、空ファイルの事です。
$ ./echo.sh > /dev/null
$
これまで標準されてたこれは標準出力です
が表示されなかった事が分かります。
これは、標準出力を/dev/null
に捨てた為です。
5. 結果をマージする
先程から出てくる>&{ファイルディスクリプタ}
という表現は何かというと、
{ファイルディスクリプタ}
に結果をマージするという意味です。
標準出力を標準エラー出力にマージする場合
1>&2
逆に標準エラー出力を標準出力にマージする場合
2>&1
このように使ってきます。
この時点で、もうタイトルの意味が分かってきますね。
タイトルにあったコマンドの意味を考えてみる
command > /dev/null 2>&1
分けて考えましょう。
-
2>&1
- 標準エラー出力の結果を標準出力にマージする
-
> /dev/null
- 標準出力を捨てる
つまり、このコマンドの意味は、
標準エラー出力の結果を標準出力にマージして、/dev/nullに捨てる
という事だったわけです。
(普通標準エラー出力は捨てない方が良いです(cronで動かす場合))
クイズ1. 標準出力を標準エラー出力として表示して下さい
ここまでくれば簡単ですね。
段階的に考えればOKです。
標準出力を標準エラー出力として表示するということは、
表示出力の結果を標準エラー出力の結果にマージすればOKです。
#/bin/bash
echo "これは標準出力を標準エラー出力として表示します" 1>&2
こんな感じですね。
$ ./echo.sh
これは標準出力を標準エラー出力として表示します
このままだと、標準出力か標準エラー出力かわかりませんね。
じゃあクイズ2で確かめて見ましょう。
クイズ2. クイズ1の結果を標準エラー出力と分かるように確認してください
さて、ではここではリダイレクトを使ってみましょう。
先程作ったコードでは、標準エラー出力が表示されているはずなので、
標準エラー出力をファイルにリダイレクトしみます。
まずは、これが標準出力ではないことを確認してみましょう。
$ ./echo.sh > result.txt
これは標準出力を標準エラー出力として表示します
$ cat result.txt
$
この場合、下記のようなことが起きてます。
- 標準出力を
result.txt
にリダイレクト - 標準エラー出力が表示
じゃあ次に、表示エラー出力であることを確認してみます。
$ ./echo.sh 2>result.txt
$ cat result.txt
これは標準出力を標準エラー出力として表示します
今度はコンソールには何も表示されず、ファイルに残りました。
これが標準エラー出力がresult.txtにリダイレクトされた結果です。
まとめ
- ファイルディスクリプタが何かを理解出来た
-
&>{ファイルディスクリプタ}
や、{ファイルディスクリプタ}&>{ファイルディスクリプタ}
の意味も理解出来た - 最初に見ていた
command > /dev/null 2>&1
がどういう意味かも分かった
補足
ただし、世間的に2つの話がアリます。
1. そもそもこの記述が分かりづらい
- まぁ、普通に見て、
2>&1
とか、1>&2
とか、初見ではイミフ - 何で
/dev/null
はリダイレクトさせてるのに、2>&1
は何も書かないの? - そもそもどっち先に書くんだっけ
とか色々あります。
これに対して色んな解決索が提示さてますが、1つは標準出力と標準エラー出力を別ファイルにリダイレクトさせるという方法
command 1>normal.txt 2>error.txt
分かりやすいですね。
実際にやってみましょう。
echo.sh
を少し改造します。
#/bin/bash
echo "これは標準出力です"
echo "これは標準出力を標準エラー出力として表示します" 1>&2
実験
$ ./echo.sh 1>normal.txt 2>error.txt
$ cat normal.txt
これは標準出力です
$ cat error.txt
これは標準出力を標準エラー出力として表示します
想定通りですね。そしてcommand > /dev/null 2>&1
よりは分かりやすいです。
もう1つは個人的な意見ですが、cronで動かすようなバッチとかは、
標準出力には出すような内容は、ログファイルに出すようにしておくのが良いかと思ってます。
2. 標準エラー出力を捨てるな
最初のcommand > /dev/null 2>&1
というのは、これまで説明してきた通り、
標準エラー出力を標準出力の結果にマージして、/dev/nullに捨てるという事をしてます。
つまり、command
(何かしらのスクリプト)で標準エラー出力すらも捨ててるので、
何か問題が起きた時に気づけないという問題があります。
cronで動かす場合は、crontab
にMAILTOを書くのが定石ですが、全て> /dev/null 2>&1
してしまっていると、このMAILTOには何も送られてこないので意味なくなります。
せめて標準エラー出力が起きたらメールで気付ける。
くらいの運用は最低限出来ておいた方が良いかと思います。
今後
これで、あなたも明日からちゃんと理解しながらcrontabを書ける事になるでしょう。
今のcrontabを見直して、おかしな表現だったり、分かりづらい表現であればチームに提言してみてはいかがでしょうか。