ひだまりソケットは壊れない

ソフトウェア開発に関する話を書きます。 最近は主に Android アプリ、Windows アプリ (UWP アプリ)、Java 関係です。

まじめなことを書くつもりでやっています。 適当なことは 「一角獣は夜に啼く」 に書いています。

cron 設定ファイル (crontab ファイル) の置き場所と書式について

上の記事を読んで興味を惹かれたので、『WEB+DB PRESS Vol.79』 の 「cron 周りのベストプラクティス」 を読んだ。 Web 上でも公開されている。 (2014-06-23 追記。)

そこでは cron 設定 (crontab) ファイルの記述方法として crontab コマンドを使用する方法が書かれていた *1。 で、記事を見てるうちに、以前 crontab コマンドやら /etc/cron.d やらの違いがよくわからずに調べて社内日記に書き残しておいたことを思いだしたのでブログで公開しておこうと思う。

ちなみに man の内容などは Debian 7 で見てるので、別ディストリビューションだと違ったりするのかもしれない。

WEB+DB PRESS Vol.79

WEB+DB PRESS Vol.79

  • 作者: 成瀬ゆい,そらは(福森匠大),西磨翁,小川航佑,佐藤新悟,塚越啓介,藤原亮,堀哲也,田村孝文,桑野章弘,松浦隼人,中村俊之,田中哲,福永亘,杉山仁則,伊藤直也,登尾徳誠,近藤宇智朗,若原祥正,松木雅幸,奥野幹也,後藤秀宣,羽二生厚美,笹田耕一,平河正博,東舘智浩,渡邊恵太,中島聡,A-Listers,はまちや2,川添貴生,山田育矢,伊藤友隆,村田賢太,まつもとゆきひろ,佐野岳人,山口恭兵,千葉俊輝,平松亮介,WEB+DB PRESS編集部
  • 出版社/メーカー: 技術評論社
  • 発売日: 2014/02/22
  • メディア: 大型本
  • この商品を含むブログ (4件) を見る

man について

本記事中で 『cron(8) の man』 などのように書いてある場合、man 8 cron という感じで man コマンドを使うことでその文書を読める。

crontab ファイルの置き場所

cron 設定をどこに書くことができるのか

cron(8) の man を見ると次のように書かれている。

cron は、スプール領域 (/var/spool/cron/crontabs) に置かれた crontab ファイルを探す (これらのファイルには /etc/passwd 内のアカウントを元にした名前がつけられている)。 見つかった crontab ファイルはメモリにロードされる。 このディレクトリの crontab ファイルは直接アクセスすべきではないことに注意せよ。 ファイルのアクセスや更新には crontab を使用すべきである。

cron は /etc/crontab も読み込む。このファイルのフォーマットは少々異なっている ( crontab(5) を参照)。 さらに cron は /etc/cron.d 内のファイルも読み込み、それらのファイルを /etc/crontab ファイルと同様に処理する (/etc/cron.d 内のファイルは /etc/crontab の特別なフォーマットに従う。つまりユーザ名のフィールドを含む)。 しかし、/etc/cron.d 内のファイルは /etc/crontab とは独立している。 つまり、例えば環境変数の設定を /etc/crontab から継承したりはしない。 この機能の用途は、/etc/cron.{daily,weekly,monthly} ディレクトリよりも細かな予定の調節を必要とするパッケージが、 /etc/cron.d に crontab ファイルを追加できるようにすることである。

ということで、cron 設定を書く場所 (crontab ファイルの置き場所) としては次の 3 つがある。

  • 各ユーザーの crontab ファイルの置き場所
    • /var/spool/cron/crontabs ディレクトリ内 (crontab コマンドで読み書きする)
  • root 権限が必要 (?) な crontab ファイルの置き場所
    • /etc/crontab ファイル
    • /etc/cron.d ディレクトリ内

crontab ファイルの書式は crontab(5) の man で見ることができる。 /var/spool/cron/crontabs 下に置く場合の書式と /etc/crontab や /etc/cron.d 下に置かれる crontab ファイルの書式は違うので注意が必要 (詳細は後述)。

crontab コマンドでユーザーごとの設定を書く

/var/spool/cron/crontabs 下のユーザーごとの crontab ファイルを読み書きする際は、crontab コマンドを使用する。 詳細は crontab(1) の man を参照のこと。

ちなみに、crontab コマンドに -r オプションを渡すと crontab が削除されてしまうため、crontab ファイルはユーザーのホームディレクトリなりなんなりでバージョン管理しておいて、crontab コマンドにそのファイルパスを渡すことで cron 設定を行う、というのがベストプラクティスとなっている。

これについては 『WEB+DB PRESS Vol.79』 の 「cron 周りのベストプラクティス」 にも書かれていた。

crontab の書式

crontab(5) の man には次のように書かれている。 実行時刻指定のフィールドの詳細については man を参照のこと。

各行には 5 つの時刻・日付フィールドがあり、 さらにコマンドと改行文字 ('\n') が続く。 (略) フィールドはスペース区切りでもタブ区切りでもかまわない。

(略)

「第 6」フィールド (行の残りの部分) には実行されるコマンドを指定する。 その行のコマンド部 (改行文字または % 文字まで) が /bin/sh (またはその crontab ファイルの SHELL 環境変数で指定されたシェル) によって実行される。 コマンド中にパーセント記号 (%) が バックスラッシュ (\) によってエスケープされずに置かれていると、 改行文字に置き換えられ、最初に現れた % 以降の全てのデータは 標準入力としてコマンドに送られる。 シェルの行末の "\" のような、 コマンドの単一の行を複数行に分割して記述する方法は、crontab にはない。

man にあるサンプルは次のような感じ。

# コマンドの実行に、デフォルトの /bin/sh ではなく /bin/bash を使用する。
SHELL=/bin/bash
# (この crontab の所有者に関らず) あらゆる出力を `paul' にメールする。
MAILTO=paul
#
# 毎日、日付変更の 5 分後に実行する
5 0 * * *       $HOME/bin/daily.job >> $HOME/tmp/out 2>&1
# 毎月初日の 2:15pm に実行する -- 出力は paul にメールされる
15 14 1 * *     $HOME/bin/monthly
# 平日の午後 10 時に実行してジョーを心配させる
0 22 * * 1-5    mail -s "午後10時だ" joe%ジョー、%%お前の子どもはどこだい?%
23 0-23/2 * * * echo "毎日 0,2,4..時 23 分に実行する"
5 4 * * sun     echo "日曜 4 時 5 分に実行する"

ジョーを心配させててひどい。

/etc 下の crontab について

/etc/crontab

/etc/crontab はシステム管理者が触るためのもので、普通はいじらない感じにしておくらしい。 デフォルト (?) では /etc/cron.daily/ や /etc/cron.weekly/ ディレクトリ内のスクリプトを定期的 (cron.daily なら 1 日ごと、cron.weekly なら 1 週間ごと) に実行するようになってるぽい。

/etc/cron.d ディレクトリ下の crontab

この中に置かれたファイルも cron による実行対象である。 cron が自動的にファイルの編集時刻をチェックしてくれるので、/etc/cron.d ディレクトリの中のファイルを変更しても crond の再起動などは必要ない。

crontab の書式

crontab(5) の man には次の記述がある。 /var/spool/cron/crontab 下の crontab の書式と違うのは、実行ユーザーの設定を行うためのフィールドがある点である。

各行には 5 つの時刻・日付フィールドがあり、 さらにコマンドと改行文字 ('\n') が続く。 システムの crontab ファイル (/etc/crontab) は同様のフォーマットを使用するが、 時刻・日付フィールドとコマンドの間で、 コマンドを実行するユーザ名を指定する。

man には次のような例が書かれていた。

SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

# m h dom mon dow user    command
42 6 * * *        root    run-parts --report /etc/cron.daily
47 6 * * 7        root    run-parts --report /etc/cron.weekly
52 6 1 * *        root    run-parts --report /etc/cron.monthly

crontab の書式に関するおまけ

標準出力とエラー出力をファイルに出力したい場合

コマンドの出力結果は、通常はメールで送信されるが、以下のようにリダイレクトしてファイルに書きだすこともできる。 エラー出力も標準出力と同じファイルに書きだしたければ 2>&1 って付けておけばよい。

* * * * * /usr/bin/perl -e'print STDOUT "test\n"; print STDERR "test error\n";' >> $HOME/cron_test.log 2>&1

実行日の決定のはまりどころ

crontab(5) の man より。

注意: コマンド実行の日は 2 つのフィールドで指定できる — 月内日および曜日である。 もし両方のフィールドが制限指定 (* 以外) であると、いずれかのフィールドが現在時刻と合った時にコマンドが実行される。 例えば、

30 4 1,15 * 5

とすると、毎月 1 日と 15 日および毎週金曜日の 午前 4:30 にコマンドが実行される。

月内日と曜日の両方を指定していると、それぞれ独立に実行日の指定に使われるので注意!! *2

crontab をどこに置くのがいいのか

Web サービスなどのための crontab ファイルを /etc/cron.d 下に置くか、ユーザーごとの crontab として設定するか、どっちがいいのだろー、と思ったりした。 会社の方針とか、cron 以外の部分の運用によってもどちらがいいのか変わってくると思うけれど。

会社の人と話して教えてもらった /etc/cron.d 下での運用の利点としては、複数ファイルに分けて管理することがしやすい、ということがある。 確かにプロジェクトあたりに大量の cron 設定があるならファイルを分けておきたい。

それと設定ファイルを探しやすいということもある。 「ちゃんと調べてないですが」 という注意付きで以下のようなコメントを貰った。

12:59 (********) /var/spool/cron ← このへんのディレクトリは実装依存です
13:00 (********) /etc/crontab とか /etc/cron.d は大抵の実装で共通だからうれしい

私は前任者が書いた crontab ファイルを探すという経験をしたことはないのだけれど、ブックマークコメントにも 「/var/spool/cron 下だと見逃すことがある」 というようなことが書かれていたので、経験者的には /etc/cron.d の方が嬉しそう、という雰囲気を感じる。

もちろん root 権限が必要であるというような欠点もあるので、まあどっちにするかは組織での運用方針次第だろうなーという気がする。

(ちなみに私は /etc/cron.d 下に置くことが多い。)

*1:もしかしたら /etc/cron.d とかについても触れられていたかもしれないけど、ざっと見た感じなかったと思う。

*2:これも 「cron 周りのベストプラクティス」 に書かれてた。