crontabの覚え書き:忘れがちな > /dev/null 2>&1 という魔法の設定読解

2020/02/19

author

masyus

バッチを開発する際、既存のcrontabの設定を眺めていると

0 * * * * sh hogehoge.sh > /dev/null 2>&1

という記述が目に入ることがあると思います。シェルスクリプトを実行するところまでは理解しやすいと思いますが、少し難解なのが

> /dev/null 2>&1

の部分です。ざっくり解説しますとcronによるコマンドの定期実行時、当該コマンドによるエラーやecho等の出力を一切出さないようにする設定ですが、その具体的な仕組みを私もよく忘れやすいため備忘録を兼ねて解説を残します。

リダイレクトを理解する

まずは

sh hogehoge.sh > /dev/null 2>&1

>についてです。これはリダイレクトといい、別の場所に出力内容を渡すことができる指定です。たとえばhoge.shというシェルスクリプトに

#!/bin/bash

echo 'aaa'

という出力が書かれていたとします。これをcliで実行すると

sh hoge.sh
aaa

という結果になるのは皆さんもすぐ理解できるかと思います。仮にこの出力内容をfuga.txtという別のファイルに追記させたい場合、下記のようにリダイレクトを活用することで実現できます。

sh hoge.sh > fuga.txt
cat fuga.txt
aaa

実際の開発現場におけるリダイレクトの使い方の例としては、ログファイルを解析して必要な情報だけ抽出したものを別のテキストファイルに吐き出したい時などによく使います。

スペシャルファイルと/dev/nullを理解する

UNIXやUnix系OS(LinuxやMac等)には スペシャルファイル(デバイスファイルとも呼ぶ) と呼ばれる、コンピュータ〜キーボードやマウス等のデバイスの仲介役を担うデバイスドライバに対してコンピュータから何らかの指令を伝達することができる特殊なファイルが存在します。その特性上、このスペシャルファイルは/dev/配下に配置されます。

スペシャルファイルは大きく3つの区分があります:

  • キャラクタデバイス
  • ブロックデバイス
  • 擬似デバイス

/dev/nullはこの中の3.に該当します。擬似デバイスは対応する物理的な装置が存在しないものをそのように呼びます(他2つの詳細は割愛します)。

ここまで解説した上で、改めて/dev/nullがどういう挙動をするスペシャルファイルなのかについて見ていきます。このスペシャルファイルは

そこに書き込まれた内容を全て捨て去る

という挙動で、その振る舞いからブラックホールと呼ばれたりもします。つまり

sh hogehoge.sh > /dev/null

とあった場合、hogehoge.sh実行時にecho等で何らかの出力があった場合、あらゆる出力を捨て去るという動きにすることが可能です。

ファイルディスクリプタを理解する

次は

sh hogehoge.sh > /dev/null 2>&1

21についてです。この数字はファイルディスクリプタと呼ばれるもので、よく使うものに下記があります:

  • 0: 標準入力(stdin: standard input)
  • 1: 標準出力(stdout: standard output)
  • 2: 標準エラー出力(stderr: standard error)

これら3つを標準ストリームといい、UNIXやUnix系OS(LinuxやMac等)に予め備わっている入出力用のチャネルです。

echoのコマンドにより出力される内容は、echo自身が上記1:標準出力に内容を渡してくれるので出力できるようになります。また、プログラムのエラー等で吐き出される内容は上記2:標準エラー出力に自動的に渡してくれることで、同様に出力できるようになります。

ファイルディスクリプタに応じてリダイレクトを適切に扱う

ここでは、標準出力だけをリダイレクトさせて指定のテキストファイルへ書き出したい場合を考えます。例えばファイルaaa.txtの名前をresult1.txtに出力させるとします。

ls aaa.txt > result1.txt
cat result1.txt
aaa.txt

上記はlsコマンド実行による標準出力をresult1.txtへリダイレクトすることで実現していますが、より詳細に書くと

ls aaa.txt 1> result2.txt
cat result2.txt
aaa.txt

と同じことをしています。つまり>1>の略で、1>と書いた場合は標準出力だけをリダイレクトさせることが可能です。

次に、標準エラー出力だけをリダイレクトさせて指定のテキストファイルへ書き出したい場合を考えます。具体的には存在しないファイル(ここではbbb.txtとする)の名前をresult2.txtに出力させるとします。

ls bbb.txt
ls bbb.txt: No such file or directory
ls bbb.txt 1> result2.txt
cat result2.txt

今回の場合、最後のcatコマンド実行時に何も表示されません。これはlsコマンド実行時に表示されたls bbb.txt: No such file or directory標準エラー出力であったためです。標準エラー出力のファイルディスクリプタが2であることを考慮すると、下記のようにコマンドを変えることでlsコマンド実行結果がcatコマンド実行時に表示されるようになります。

ls bbb.txt
ls bbb.txt: No such file or directory
ls bbb.txt 2> result2.txt
cat result2.txt
ls: bbb.txt: No such file or directory

&を併用することで、特定の出力を指定した出力へマージする

さきほどリダイレクト結果を出力できなかった

ls bbb.txt
ls bbb.txt: No such file or directory
ls bbb.txt 1> result2.txt
cat result2.txt

を、標準出力すること自体は維持しつつ、標準エラーとして出力される内容も標準出力へマージして書き出させる方法があります。それが&を使うことです。具体的には下記のようになります。

ls bbb.txt
ls bbb.txt: No such file or directory
ls bbb.txt 1> result2.txt 2>&1
cat result2.txt
ls: bbb.txt: No such file or directory

総論

以上の内容を踏まえますと、

0 * * * * sh hogehoge.sh > /dev/null 2>&1

と書かれていた場合、下記のように解釈すれば良いです。

毎時0分に定期実行されるhogehoge.shのシェルスクリプト実行における標準出力および標準エラー出力を、どこにも書き出さずに処理を終了させる

少々長い解説でしたが今回はこれで以上となります。