crontabの書き方:Linuxのスケジュールタスクの例と構文
crontabは、cronデーモンがコマンドを実行するスケジュール表です。各行は5つの時刻フィールド(分、時、日、月、曜日)とコマンドから成ります。たとえば */5 * * * * /opt/backup.sh は5分ごとにスクリプトを実行します。編集には crontab -e を使います。多くの問題は、cronの環境が制限されていることに起因します。フルパスを指定し、出力をログに保存してください。
cronはLinuxの標準的なタスクスケジューラーです。cron デーモンは毎分、スケジュール表(crontab)を読み込み、実行時刻になったコマンドを起動します。夜間のバックアップ、一時ファイルの削除、証明書の更新、レポートの送信などに使われます。以下では、crontabの構文、よくある用途のcrontabの書き方の例、そしてジョブが「黙って」実行されない原因を紹介します。
crontabを開いて編集する
crontabはユーザーごとに存在します。/var/spool/cron のファイルを直接編集するのではなく、crontab コマンドを使って扱います。
| コマンド | 動作 |
|---|---|
crontab -e | 自分のcrontabをエディターで開く(初回はエディターの選択を求められる) |
crontab -l | 現在のcrontabを表示する |
crontab -r | crontabを確認なしで全削除する |
crontab -i -r | 確認を求めてから削除する(Debian/Ubuntu、RHEL) |
sudo crontab -u alice -e | 別のユーザーのcrontabを編集する |
crontab ファイル | ファイルの内容でcrontabを置き換える |
crontab -e で保存すると、構文がチェックされ、crontab: installing new crontab と表示されます。行に誤りがあると、errors in crontab file, can't install と表示され、もう一度編集するよう促されます。cronデーモンの再起動は不要です。変更は自動的に反映されます。
-r には注意してください。e と r はキーボード上で隣り合っており、バックアップもありません。次のようにバックアップを取っておく習慣をつけると安心です。
crontab -l > ~/crontab.bak
# 復元
crontab ~/crontab.bak
crontabの構文:5つの時刻フィールド
各ジョブの行は次の形式です。
# ┌───────── 分 (0–59)
# │ ┌─────── 時 (0–23)
# │ │ ┌───── 日 (1–31)
# │ │ │ ┌─── 月 (1–12 または jan–dec)
# │ │ │ │ ┌─ 曜日 (0–7 または sun–sat。0と7は日曜日)
# │ │ │ │ │
* * * * * コマンド
各フィールドでは、次の特殊文字を使えます。
| 記号 | 意味 | 例 |
|---|---|---|
* | 任意の値 | * * * * * は毎分 |
, | リスト | 0 9,18 * * * は9時と18時 |
- | 範囲 | 0 9-18 * * * は9時から18時まで毎時 |
/ | 間隔 | */15 * * * * は15分ごと |
間隔は範囲と組み合わせられます。0 8-20/2 * * * は8時から20時まで2時間ごと(8、10、…、20時)です。時刻はシステムのタイムゾーンで解釈されます。timedatectl で確認してください。
毎日使えるcrontabの例
| スケジュール | crontabの行 |
|---|---|
| 毎分 | * * * * * |
| 5分ごと | */5 * * * * |
| 30分ごと | */30 * * * * または 0,30 * * * * |
| 毎時0分 | 0 * * * * |
| 6時間ごと | 0 */6 * * * |
| 毎日3:00 | 0 3 * * * |
| 毎日23:45 | 45 23 * * * |
| 平日の9:00 | 0 9 * * 1-5 |
| 週末の12:00 | 0 12 * * 6,0 |
| 毎週月曜日の8:30 | 30 8 * * 1 |
| 毎月1日の0:00 | 0 0 1 * * |
| 四半期ごと | 0 0 1 1,4,7,10 * |
| 毎年1月1日 | 0 0 1 1 * |
| 営業時間中の10分ごと | */10 9-18 * * 1-5 |
ジョブの完全な行は、スケジュールとコマンドの組み合わせです。
# 毎晩3:00にデータベースのバックアップ
0 3 * * * /usr/local/bin/db-backup.sh >> /var/log/db-backup.log 2>&1
# 日曜日の4:00に、7日より古い一時ファイルを削除
0 4 * * 0 /usr/bin/find /tmp/app -type f -mtime +7 -delete
# 5分ごとにサイトの死活監視
*/5 * * * * /usr/bin/curl -fsS https://example.com/health > /dev/null || /usr/local/bin/alert.sh
@reboot、@daily などの特殊な記述
5つのフィールドの代わりに、短い別名を使うこともできます。
| 別名 | 同等の記述 |
|---|---|
@reboot | cronデーモンの起動時(システム起動後)に1回だけ |
@hourly | 0 * * * * |
@daily / @midnight | 0 0 * * * |
@weekly | 0 0 * * 0 |
@monthly | 0 0 1 * * |
@yearly / @annually | 0 0 1 1 * |
@reboot /home/alice/bin/start-tunnel.sh >> /home/alice/tunnel.log 2>&1
常に動いていて、停止したら再起動されるべきサービスには、@reboot は不向きです。その場合は、Restart=on-failure を設定したsystemdのユニットを作成してください。
N秒ごとに実行するには
cronの最小単位は1分です。それより頻繁に実行する必要がある場合は、2つの方法があります。1つ目は、sleep で時間をずらした複数の行を書く方法です。
* * * * * /opt/check.sh
* * * * * sleep 30; /opt/check.sh
2つ目は、より適切な方法で、OnUnitActiveSec=30s を指定したsystemdタイマーを使う方法です。ジョブを1分より頻繁に実行する必要がある場合、それはcronではなくデーモンやタイマーの仕事であることが多いです。
システムのcrontab:/etc/crontab と /etc/cron.d
ユーザーのcrontabのほかに、システム全体のcrontabもあります。その形式は少し異なります。時刻とコマンドの間に、コマンドを実行するユーザー名を書きます。
# /etc/cron.d/certbot-renew
SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
0 */12 * * * root certbot renew --quiet
それぞれの場所の役割は次のとおりです。
/etc/crontab:システムの主要なcrontab。DebianやUbuntuでは、下のディレクトリのジョブもここから実行されます。/etc/cron.d/:ジョブを記述する個別のファイル。パッケージが配置したり、Ansibleなどで管理したりするのに便利です。/etc/cron.hourly/、cron.daily/、cron.weekly/、cron.monthly/:スケジュールのない実行可能なスクリプトを置く場所です。run-parts(またはanacron)によって実行されます。
run-parts の注意点として、DebianやUbuntuでは、名前にドットを含むファイルは無視されます。たとえば /etc/cron.daily/ に置いた backup.sh は実行されません。名前を backup に変更してください。実行される対象は run-parts --test /etc/cron.daily で確認できます。
/etc/cron.d/ のファイルは、rootが所有し、グループや他のユーザーから書き込み可能になっておらず、末尾が改行で終わっている必要があります。そうでないと、cronはそのファイルを無視します。
cronの環境:PATH、SHELL、変数
「ターミナルでは動くのに、cronでは動かない」という問題の主な原因は、環境の違いです。cronは /bin/sh を使ってコマンドを実行し、.bashrc や .profile は読み込みません。また、PATH は通常 /usr/bin:/bin と最小限です。そのため、/usr/local/bin にある docker、node、php が見つからないことがあります。
変数はcrontabの先頭で設定できます。
SHELL=/bin/bash
PATH=/usr/local/bin:/usr/bin:/bin
MAILTO=admin@example.com
CRON_TZ=Asia/Tokyo
0 6 * * * cd /srv/app && ./report.sh
MAILTO:ジョブの出力を送るメールアドレス。MAILTO=""にするとメールを送りません。CRON_TZ:スケジュールのタイムゾーン(RHELやFedoraのcronieはサポートしていますが、Debian版のcronでは動作しません)。- 変数の値に
$HOME/binのような展開はありません。パスは完全な形で書いてください。
最も確実なのは、ジョブ内で使うすべてのプログラムとファイルに絶対パスを指定し、スクリプトの1行目にshebangの #!/bin/bash を書くことです。
cronのログの確認方法
cronはジョブが起動されたという事実をログに記録しますが、ジョブの出力は記録しません。
# DebianやUbuntu
grep CRON /var/log/syslog
# systemd-journaldを使う場合
journalctl -u cron --since today # DebianやUbuntu
journalctl -u crond --since today # RHEL、Fedora、Rocky
# journaldがないRHEL
tail -f /var/log/cron
起動の記録は、次のような形で表示されます。
Oct 14 03:00:01 web1 CRON[21544]: (root) CMD (/usr/local/bin/db-backup.sh >> /var/log/db-backup.log 2>&1)
ジョブが何を出力し、どのようなエラーで失敗したかを確認するには、標準出力と標準エラー出力の両方をファイルにリダイレクトします。>> /var/log/job.log 2>&1 を付けてください。これがないと、出力はローカルのメールで送られ、メールエージェントがインストールされていなければ失われます(ジャーナルには No MTA installed, discarding output と記録されます)。
cronがジョブを実行しない理由:よくあるミス
コマンドが見つからない
症状:ジョブのログに command not found と表示されます。原因は、PATH が制限されていることです。解決方法は、プログラムのフルパスを指定するか(which docker で確認できます)、crontabの先頭で PATH= を設定することです。
コマンド内のパーセント記号
crontabでは、% は改行を意味します。最初の % 以降の内容は、標準入力としてコマンドに渡されます。そのため、ファイル名に日付を含む典型的な行は壊れます。
# 誤り:最初の % でコマンドが切れる
0 2 * * * tar czf /backup/site-$(date +%F).tar.gz /var/www
# 正しい:エスケープする
0 2 * * * tar czf /backup/site-$(date +\%F).tar.gz /var/www
さらに簡単なのは、処理をスクリプトに移すことです。スクリプトの中では % をエスケープする必要はありません。
スクリプトが実行可能でない、または改行コードが不正
Permission denied は、ファイルに実行ビット x がないことを意味します。chmod +x script.sh を実行するか、/bin/bash /opt/script.sh のように明示的に実行してください。スクリプトをWindowsで作成した場合、CRLFの改行コードが原因で /bin/bash^M: bad interpreter という奇妙なエラーが出ます。sed -i 's/\r$//' script.sh または dos2unix で修正できます。
相対パスと作業ディレクトリ
ジョブはユーザーのホームディレクトリで開始されます。スクリプトが ./config.ini を読み込む場合、~ の中を探します。コマンドの前に cd /srv/app && を書くか、スクリプト内で絶対パスを使ってください。
日と曜日の両方を指定した場合
日と曜日の両方を指定すると、cronは両方が一致するときではなく、どちらかが一致したときに実行します。0 9 13 * 5 は、13日および毎週金曜日に実行されます。「金曜日の13日」だけではありません。必要な条件はコマンド内で確認してください。
0 9 13 * * [ "$(date +\%u)" = 5 ] && /opt/friday13.sh
ジョブが重なって実行される
バックアップが間隔より長くかかると、次の実行が前の実行に重なってしまいます。防ぐには flock でロックを使います。
*/10 * * * * /usr/bin/flock -n /tmp/sync.lock /opt/sync.sh
-n を付けると、最初のインスタンスがまだ動いている場合、2つ目は即座に終了します。
cronデーモンが動いていない
systemctl status cron で確認してください(RHELでは crond)。Dockerコンテナのような最小構成のイメージでは、cronデーモン自体がないことがあります。その場合、crontabに書いたジョブは誰にも実行されません。
cronのジョブをデバッグする
cronにできるだけ近い環境でコマンドを実行します。
env -i SHELL=/bin/sh PATH=/usr/bin:/bin HOME=$HOME /bin/sh -c '/opt/backup.sh'
ここでエラーが出れば、cronでも同じエラーが出ます。一時的に * * * * * のスケジュールを設定し、出力をファイルにリダイレクトして、tail -f で追跡すると確実です。スケジュール式の確認には、オンラインのcron式チェッカーも役立ちますが、最終的な判断はシステムのログで行ってください。
cronの利用を制限する:cron.allow と cron.deny
利用できるユーザーは、/etc/cron.allow と /etc/cron.deny で制限されます。cron.allow が存在する場合、そこに記載されたユーザーだけがcrontabを使えます。cron.allow がなく、cron.deny がある場合は、そこに記載されたユーザー以外が使えます。拒否されたユーザーが crontab -e を実行すると、You (bob) are not allowed to use this program と表示されます。
まとめ
crontabは、5つの時刻フィールドとコマンドから成る行です。*/5 * * * * は5分ごと、0 3 * * * は毎晩、0 9 * * 1-5 は平日の9時を意味します。編集は crontab -e で行い、絶対パスを使い、% をエスケープし、出力をログに保存し、時間のかかるジョブは flock で保護してください。ここで紹介したcrontabの例で、サーバーのほとんどの定期的な作業は対応できます。自動化とスケジュール管理は、Linuxコースで実践的に解説しています。
よくある質問
cronとanacronの違いは何ですか?
cronは正確な時刻にジョブを実行し、コンピューターが電源オフだった場合は実行をスキップします。anacronは、起動後に日次、週次、月次のジョブが必ず実行されることを保証するため、ノートPCやデスクトップ向けです。
cronとsystemdのタイマーのどちらを使うべきですか?
cronはシンプルで、ほぼすべての環境にあります。systemdのタイマーは、journalctlでのログ、依存関係、リソースの制限、取り逃したジョブを実行する Persistent=true などの機能を備えているため、最近のサーバーでは重要なジョブに使われることが増えています。
ユーザーのcrontabは物理的にどこに保存されていますか?
DebianやUbuntuでは /var/spool/cron/crontabs/ユーザー名、RHELやFedoraでは /var/spool/cron/ユーザー名 にあります。このファイルは直接編集せず、crontab -e を使ってください。
crontabのジョブを一時的に無効にするにはどうすればよいですか?
crontab -e で該当の行の先頭に # を付けてコメントアウトします。ユーザーのすべてのジョブを止めるには、crontab -l で内容をファイルに保存してから、crontab -r で削除します。
月の最終日にジョブを実行するにはどうすればよいですか?
専用のフィールドはありません。28日から31日に実行するように設定し、翌日が1日かどうかをチェックします:0 23 28-31 * * [ "$(date -d tomorrow +\%d)" = 01 ] && /opt/job.sh