コマンド
crontab
スケジュールを登録。決められた日時・時刻にプログラムを実行する。クーロン。クーロンタブ。
最終更新
UNIX/Linux の crontab コマンドは定期的に実行するコマンドを登録・確認・管理するコマンドである。
UNIX・Linux では cron または crond というデーモンが常時起動しており、指定の時刻になると指定のコマンドを実行してくれる。crontab コマンドを使うと、その設定ファイルを表示・設定・更新・削除することができる。
●crontab 設定方法
日時を指定して、自動的にコマンドを実行することができる。設定方法は
とすると、エディタが起動して設定内容を更新することができる。エディタは環境変数 EDITOR で自由に設定することができる。しかしながら、e をタイプするつもりが隣の r を押してしまうと、crontab -r で設定内容がクリアされてしまう。当ページ管理人は「crontab -e を使うな」と声を大にして言いたい。
おすすめは、~/.crontab や crontab.txt などのファイルを作り、その中に設定内容を記述した上で、
% crontab 設定ファイル名
→ FreeBSD・Linux の場合
% crontab < 設定ファイル名
→ Solaris の場合
とする方法である。当ページ管理人は、~/.crontab というファイルを作り、このファイルをエディタで編集してから、
として cron に反映する方法を好む。ただし、この方法だと cron に登録されているデータと ~/.crontab の内容が一致している保証はない、という欠点がある。
●crontab 設定ファイル書式・書き方
crontab 設定ファイルの書き方を説明する。書式は
である。それぞれの書式は下記のとおり。
分 0-59
時 0-23
日 1-31
月 1-12
曜日 0-7 (0:日曜、1:月曜、2:火曜、3:水曜、4:木曜、5:金曜、6:土曜。7 は 0 と同じ日曜)
それぞれの値は、カンマで結合することで複数指定することができる。例えば「時」のカラムを "6,12,18" とすると、6時・12時・18時を指定できる。また、"2-10" などで範囲指定ができる。さらに FreeBSD・Linux などの一部の crontab では「6-18/2」「*/3」などと、スラッシュを使うことで「N日おき」「N時間おき」「N分おき」といった指定ができる。
いくつかの例を下記に示す。
* * * * * command
⇒ 1分おき (毎分) に command を実行する。
10 3 1 * * command
⇒ 毎月1日の3時10分に command を実行する。
0 12 * * 0 command
⇒ 毎週日曜日の12時に command を実行する。
0 0,4,8,12,16,20 * * * command
⇒ 0時0分・4時0分・8時0分・12時0分・16時0分・20時0分 に command を実行する。
0 */4 * * * command
⇒ 上記と同じことは "*/4" と書いてもよい ("*/4" は 4時間おきの意味)
10 6-20 * * * command
⇒ 6時〜20時の間、毎時10分に command を実行する (6時10分〜20時10分)。
10 6-20/2 * * * command
⇒ 6時〜20時の間、2時間おきに、毎時10分に command を実行する (6時10分・8時10分〜18時10分・20時10分)。
実行結果はメールで自分自身に知らされる。メールを送ってほしくない場合は、
* * * * * command 1> /dev/null
と標準出力を捨てればよい。この場合はエラーが起こった時だけ (標準エラー出力に出力があった時だけ)メールが送られてくるので便利である。エラーが起ころうと絶対にメールを送ってほしくない場合は
* * * * * command >/dev/null 2>&1
とすればよい。
●cron における環境変数 【2018-12-14 更新】
crontab で実行されるときには、ほとんどの環境変数が設定されていないので注意が必要。~/.cshrc・~/.login・~/.bash_profile 等で設定している環境変数は設定されていないし、PATH も /bin:/usr/bin くらいにしか通っていない。
そのため、command の部分に指定するのはシェルスクリプトにしておいて、シェルスクリプトの先頭で必要な環境変数の設定を行い、その後いろいろなコマンドを実行するようにしておくのがよいだろう。
ただし FreeBSD・Linux 等の cron においては、
PATH=/bin:/usr/bin:/usr/sbin:/usr/local/bin
HOGE=fuga
* * * * * command
などと crontab 定義内にて変数設定を行うことができる。Solaris や HP-UX では変数定義はできない。
なお、CentOS 6.8 にて設定されていた環境変数は以下のとおり。
HOME=/home/68user
LANG=ja_JP.utf-8
LOGNAME=68user
PATH=/usr/bin:/bin
PWD=/home/68user
SHELL=/bin/sh
SHLVL=1
USER=68user
_=/usr/bin/env
これは下記の crontab 定義で確認した。
●crontab コマンドのオプション一覧
▷ -l オプション
現在設定されている crontab データを表示する
▷ -r オプション
現在設定されている crontab データを削除する。
▷ -e オプション
現在設定されている crontab データを編集する (エディタが起動する)。
▷ -u [ユーザ名]
一般ユーザで crontab コマンドを実行する場合、自分自身の crontab データのみ表示・修正することができる。root で他ユーザの crontab データを更新したい場合、-u オプションでユーザを指定する。
# crontab -u yamada -e
→ ユーザ yamada の crontab データを修正する。
# crontab -u yamada -r
→ ユーザ yamada の crontab データを削除する。
cron を設定した後は、必ず crontab -l で確認すること。もし動作がおかしいなら crontab -r で設定をクリアできるが、その後も必ず crontab -l で確認しよう。
これは一見、「5で割り切れたら実行」や「5分おきに実行」と思えるが、そうではない。
- "*/5" は、"*" について「1番目・6番目・11番目・16番目…」を実行する
- "10-50/10" は、"10-50" について「1番目・11番目・21番目・31番目…」を実行する
という意味である。
"*" は 0,1,2,3,4,5,6…59 と等価なので、結果的に 0分・5分・10分・15分…の起動となるし、"10-50" は 10,11,12…50 と等価なので、結果的に 10分・20分・30分・40分…の起動となる。
少し注意が必要なのは、"*/8" は 0分・8分・16分…56分 と起動間隔が 8分おきになるが、N時56分と N+1時0分の間は 4分間しかない。起動プログラムの処理時間次第では、1時間に一度だけ重複起動してしまうこともありうる。微妙な間隔を指定する場合は注意してほしい。
●複数コマンドの順次実行・並列実行
cron の設定ファイルでは、
* * * * * cd foo/bar; command
というふうに、セミコロンで区切ることで複数のコマンドを記述することができる。その他にもシェルが解釈できる構文や * ? などのメタキャラクタも使用可能である。これは cron は書かれた行をそのままシェルに渡すからである。標準のシェルは sh なので、sh が解釈できるものは何でも書けるというわけだ。
前処理のプログラムが正常終了した場合のみ、次のプログラムを実行したい場合は以下のようにする。
* * * * * command1 && command2
前処理プログラム A と B を並列実行し、A・B 両方が終了したらプログラム C を実行するには以下のようにする。
* * * * * (commandA & commandB & wait) && commandC
●csh での実行 【2018/12/14 追加】
crontab に記述したコマンドは /bin/sh にて実行される。これは、cron からは sh スクリプトしか実行できないという意味ではなく、
の "command >& /dev/null" は /bin/sh のコマンドラインとして解釈されるという意味である (command が csh スクリプトでも python スクリプトでも構わない)。
よって、下記のように csh の書き方でリダイレクトを書いても、/bin/sh で解釈できないのでエラーとなる。
* * * * * command >& /dev/null
ただし、設定ファイルの先頭で
などと書くと、sh でなく csh に渡されることになり、
* * * * * command >& /dev/null
といった csh 風の書き方ができるようになる (少なくとも FreeBSD で確認済)。
●秒単位での指定
cron では、1分より細かい単位で実行することはできない。もし 30秒ごとにコマンドを実行したい場合は
* * * * * command
* * * * * sleep 30 ; command
などと書けばよい。
●@reboot で OS 起動時の処理
一部 cron 実装 (vixie-cron の系統を採用している FreeBSD・Linux 等) では、@reboot という書き方によって、OS 起動時に任意の処理を行うことができる。
ただし、OS 起動だけではなく crond (デーモン) が起動した際、例えば
などとした際に @reboot が発動するかは実装によるので確認してほしい。
●メール送信時のエンコーディング変換
メールは ISO-2022-JP (いわゆる JIS コード) で送るというきまりがあるので、出力が EUC-JP や Shift_JIS などの日本語を含む場合は、
* * * * * command | nkf -j
と、nkf などで ISO-2022-JP に変換すればよい。
●注意点: 日と曜日指定は OR である
極めてわかりづらいのだが、日と曜日の組み合わせだけは「OR」である。
は、毎月1日の 0時0分と、毎週火曜日の 0時0分に実行される。よって、crontab の通常書式だけでは「13日の金曜日」を表現することはできない。当ページ管理人のネット上の観測によると、これにハマる人はとても多いようなので要注意である。
●注意点: 年の指定
crontab で年の指定はできない。「今年のある処理は2017年3月18日に起動するが、来年は未定。そもそも来年実行するかどうかも未定」となったとしても、
と年なしで書かなくてはならない。来年忘れそうで不安である。そういときは
0 0 18 3 * [ `date +\%Y` -eq 2017 ] && command
としてはどうか。上記コードは未テストなので注意。
●注意点: 誤って毎分起動
毎日12時に起動しようとして、
と書くつもりが、
と書いてしまい、12時00分〜12時59分の毎分起動になってしまうミスはありがちである。しかも排他処理をサボっていたりすると、プロセス終了前に次のプロセスが起動してしまい、数十個のプロセスが実行中になり、負荷が高くなってしまうのもありがちである。
●注意点: 異なる時間での起動
毎日 8:10 と 12:50 などと異なる時分でに同じコマンドを起動するには、下記のように 2行に分けて記述する。
0 10 8 * * command
0 50 12 * * command
下記のように無理矢理書く方法はあるが、可読性が低いし、高負荷時に date コマンド実行までに 1分以上経過してしまい、分が一致しない可能性が捨てきれないため、当ページ管理人はおすすめしない。
0 10,50 8,12 [ `date +\%H\%M` = "0810" -o `date +\%H\%M` = "1250" ] * * command
●注意点: % のエスケープ
上記で
0 18 3 * [ `date +\%Y` -eq 2017 ] && command
と書いたが、crontab データ上では % は改行を表すので、"date +%Y" は "date +\%Y" とエスケープする必要がある。
●注意点: 曜日指定の環境依存 【2018/12/14 追加】
曜日の指定は下記であるが、7 が使えるのは FreeBSD・Linux 等一部の環境のみである。
曜日 0-7 (0:日曜、1:月曜、2:火曜、3:水曜、4:木曜、5:金曜、6:土曜。7 は 0 と同じ日曜)
例えば Solaris や HP-UX では 7 は使えず、0〜6 のみである。
●注意点: 変数展開 【2018/12/14 追加】
/home/user/bin/hoge を実行したくて、
BINDIR=$HOME/bin
* * * * * $BINDIR/hoge
などと書いた場合、cron は変数代入時に変数展開をしてくれないため、BINDIR は /home/user/bin ではなく '$HOME/bin' という文字列になる。その結果、'$HOME/bin/hoge' というコマンドを実行しようとして、
/bin/sh: $HOME/bin/hoge: そのようなファイルやディレクトリはありません
といったエラーになってしまう。
変数展開してくれないのはあくまで cron による変数代入時であるため、下記のように /bin/sh にて変数展開した上で、サブシェルに解釈させる方法はある。
* * * * * BINDIR=$HOME/bin sh -c '$BINDIR/hoge'
また、Linux で同様に最終行を改行コードなしとすると、crontab 編集終了後に
crontab: installing new crontab "/tmp/crontab.xxx":1: premature EOF
errors in crontab file, can't install.
Do you want to retry the same edit?
となる。ここで y を押すと再度編集となる (n とすると /tmp/ にファイルを残したまま終了し、crontab ファイルは更新されないため、改行コードなしの行を crond に実行させることはできない)。
なお、ファイル末尾の改行コードをつけ忘れた結果、意図した挙動にならないというのは UNIX/Linux コマンドにありがちな話であるので、crontab コマンドに限らず注意してほしい。
●cron の欠点
cron の欠点は以下の通り。
- 1分単位でしか起動できない。例えば n 秒おきに起動、ということができない (できなくはないが、Tips3 のように小細工が必要になる)。
- 登録した時刻にマシンが起動していないと、永遠に起動されない。
- 指定した日が祝日なら、次の営業日に実行、というちょっと凝った指定ができない。
- 重複起動防止の機能はない。プログラムを毎分実行するように設定したとして、もしプログラムの実行に 1分以上かかってしまっても、新たにプロセスがどんどん生成される。重複起動を避けたい場合は、個々のプログラムで排他制御をする必要がある。
- Tips.4 のように、cron にて前処理・後処理を行うことは可能だが、「前処理のプログラムが異常終了した場合に手動リカバリ後にその次のプログラムから再開」といった賢いリカバリ作業はできない。
cron は中小規模のシステムでは全く問題ないものの、大規模な商用システムでは上記のような欠点があるために、JP1・Senju(千手)・Hinemos・Tivoli などのジョブ管理ツールを使うことが多い。例えば、
0時からバッチA→バッチB→バッチC を実行し、3時からバッチD→バッチE を実行し、バッチC とバッチE の完了を待ってバッチFを実行するときにバッチBが異常終了した際、夜間監視を行なっているオペレータがバッチB以降を再実行する
といった運用があったとして、cron なりコマンドラインなりで完結させるのはなかなかに難しい。上記のジョブ管理ツールは GUI 画面を提供し、どの箇所でエラーになったのか・エラー内容は何かをわかりやすく表示し、ボタンひとつで再実行できたりする。