ブログに戻る

公開日 · 読了時間 2 分

ゼロから学ぶcron式: ビジュアルガイド

5フィールドのPOSIX cron文法、特殊文字、ステップ値、Quartzの拡張、AWSのcronとの違い、そして今日crontabにコピペできる十数個の実例を学びましょう。

なぜcronは今もどこにでもあるのか

cronは1979年、Version 7 UnixのためにブライアンカーニハンによってBrian Kernighanによって書かれました。最新のLinuxシステムが今も同梱している版、1987年にPaul Vixieが書いたVixie cronは、環境変数、ユーザーごとのcrontab、そして親切な@rebootショートカットを追加しましたが、元の5フィールドのスケジュール形式はほぼ手つかずのままです。その安定性こそが、World Wide Web以前に設計された文法が、2026年になってもKubernetesマニフェスト、GitHub Actionsワークフロー、AWS EventBridgeのルール、SpringのScheduledアノテーションに貼り付けられている理由です。

Stack Overflowから行をコピペするだけでcronを学ぶと、スケジュールは遅かれ早かれ予想外の動きをします。毎日のバックアップが毎分走ったり、週末のジョブが火曜日に発火したり、夏時間の境界でタイムゾーンがずれたり。本ガイドではゼロから文法をたどり、その後で十分な実例を示し、任意の式の形を一目で見抜けるようにします。

5フィールド形式

POSIX crontabの行は、スケジュールに続けてコマンドを書きます。スケジュールはちょうど5つの空白区切りフィールドです: 分、時、日(月内)、月、曜日。各フィールドは数値、リスト、範囲、ステップ、またはワイルドカード*を受け付けます。

* * * * *  command-to-run
┬ ┬ ┬ ┬ ┬
│ │ │ │ │
│ │ │ │ └─── 曜日       (0-6, 日曜 = 0; ほとんどの実装では7も日曜)
│ │ │ └───── 月         (1-12 または JAN-DEC)
│ │ └─────── 日(月内)   (1-31)
│ └───────── 時         (0-23)
└─────────── 分         (0-59)

特殊文字: * , - / とその仲間

古典的なcronでは5つの文字でほぼすべての仕事をします。それぞれの意味を覚えれば、ほとんどの式は文章のように読めます。

  • * — このフィールドのすべての値。分フィールドの*は「毎分」を意味します。
  • , — 離散値のリスト。分フィールドの0,15,30,45は1時間に固定された4分を意味します。
  • - — 両端を含む範囲。曜日フィールドの1-5は月~金を意味します。
  • / — ステップ値。range/stepまたは*/stepと書きます。分の*/10は「0から始まる10分ごと」を意味します。
  • L, W, # — Quartzの拡張で、POSIXの一部ではありません。L = 最後、W = 最も近い平日、# = 月のN番目の曜日。詳細は後述。
0,30 * * * *      毎時 :00 と :30
*/5 * * * *       5分ごと (0, 5, 10, 15 ...)
0 9-17 * * 1-5    月-金、09:00から17:00まで毎時
0 0 1,15 * *      毎月1日と15日の深夜
15 14 * * 0       毎週日曜14:15

ステップ値は思っているものとは違う

ステップ演算子/は範囲が暗黙の場合でも、常に範囲に対して働きます。分フィールドの*/15は0-59/15の省略形で、0、15、30、45で発火します。だから分の*/7は「今から7分ごと」ではなく「0~59の中の7の倍数」を意味し、0、7、14、21、28、35、42、49、56となり、深夜への切替前に4分の隙間ができます。本当のローリング間隔が必要なら、ジョブランナーやOnUnitActiveSec付きのsystemdタイマーを使ってください。

ステップは範囲の途中から始めることもできます。分の5-59/10は5、15、25、35、45、55で発火します。長いカンマリストを書かずに非対称なスケジュールを作りたいときに、範囲とステップを組み合わせてください。

*/15 * * * *      00, 15, 30, 45  (良い)
*/7 * * * *       00, 07, 14, 21, 28, 35, 42, 49, 56, その後4分の隙間 (たぶん意図と違う)
5-59/10 * * * *   05, 15, 25, 35, 45, 55
0 */4 * * *       00:00, 04:00, 08:00, 12:00, 16:00, 20:00

日(月内)と曜日: ORルール

cron最大の落とし穴: 日(月内)と曜日の両方が制限されている(どちらも*でない)場合、Vixie cronは論理ANDではなく論理ORとして扱います。0 0 13 * 5は毎月13日の深夜AND毎週金曜日に発火し、13日の金曜日だけではありません。AND動作を得たい場合は、2つのフィールドのうち1つだけを制限してもう1つはスクリプト側でフィルターするか、「特定値なし」を表す?プレースホルダーをサポートするQuartzのようなスケジューラを使ってください。

0 0 13 * 5        毎月13日の深夜 OR 毎週金曜  (Vixie cronのORルール)
0 0 * * 5         毎週金曜の深夜
0 0 13 * *        毎月13日の深夜
# Quartz: 0 0 0 13 * 5 ?   なら13日の金曜日に特定的にマッチ

コピペ可能な10個の例

この表を印刷してモニターの横に貼ってください。あなたが書く本番のcron行のほとんどは、これらのわずかなバリエーションです。

* * * * *           毎分
0 * * * *           毎時 :00
*/15 * * * *        15分ごと
0 0 * * *           毎日深夜 (サーバーローカル時間)
0 9 * * 1-5         平日09:00
30 14 * * 1-5       平日14:30
0 9 * * 1           毎週月曜09:00
0 22 * * 0          毎週日曜22:00
0 0 1 * *           毎月1日の深夜
0 0 1 1 *           1月1日の深夜
0 3 * * 6           毎週土曜03:00 (典型的なバックアップ窓)
*/5 9-17 * * 1-5    平日の業務時間中、5分ごと

名前付きショートカット

Vixie cron、GNU mcron、そしてほとんどの派生はいくつかの@プレフィックス付きエイリアスを受け付け、よくあるスケジュールに対応づけます。等価な5フィールドより読みやすく、誤字も起こりにくいです。

@yearly    0 0 1 1 * と同じ      (@annuallyも同じ)
@monthly   0 0 1 * * と同じ
@weekly    0 0 * * 0 と同じ
@daily     0 0 * * * と同じ      (@midnightも同じ)
@hourly    0 * * * * と同じ
@reboot    システム起動時に1回実行

Quartz: 6・7フィールドのいとこ

Javaのスケジューラ — Quartz本体、Springの@Scheduled(cron = ...)、いくつかのクラウドプラットフォーム — は別の方言を使います。Quartzは秒フィールドを先頭に追加し、必要に応じて年フィールドを末尾に追加し、合計6または7フィールドになります。L(最後)、W(最も近い平日)、#(月のN番目の曜日)、そして「特定値なし、日と曜日のどちらか一方だけが指定されている」を意味する?プレースホルダーもサポートします。

Quartzの式をLinuxのcrontabにコピーすると静かに誤解釈されます。先頭の0は通常の分の値に見えるからです。ファイルを保存する前に、スケジューラがどの方言を期待しているか必ず確認してください。

Quartzのフィールド: 秒 分 時 日 月 曜日 [年]

0  0  12  *  *  ?           毎日正午
0  15 10  ?  *  MON-FRI     平日10:15
0  0  0   L  *  ?           毎月最終日の深夜
0  0  0   LW *  ?           毎月最終平日
0  0  9   ?  *  2#1         毎月第1月曜の09:00
0  0  9   ?  *  6#3         毎月第3金曜の09:00
0  0  0   1  *  ?  2026     毎月1日の深夜、ただし2026年のみ

AWSのrate vs cron式

EventBridge、CloudWatch Events、LambdaはQuartzに近いcron文法を使いますが、2つの重要な差があります。秒フィールドはなく(7ではなく6フィールド)、日(月内)か曜日のどちらかに?を必ず使う必要があります(両方を*にはできません)。AWSは単純な間隔向けにrate(value unit)も提供しており、ORの落とし穴を完全に回避できます。

AWS cronのフィールド: 分 時 日(月内) 月 曜日 年

cron(0 12 * * ? *)        毎日12:00 UTC
cron(0/15 * * * ? *)      15分ごと
cron(0 9 ? * MON-FRI *)   平日09:00 UTC
cron(0 0 1 * ? *)         毎月1日のUTC深夜

rate(5 minutes)           5分ごと
rate(1 hour)              毎時
rate(7 days)              ルール作成から7日ごと

タイムゾーン、夏時間、02:30に壊れるもの

Vixie cronはシステムのタイムゾーン(TZ環境変数か/etc/localtimeが指すもの)に従います。夏時間で1時間が飛ばされると、その隙間に予定されたジョブは静かにスキップされます。標準時に戻るときは、重複した時間内のジョブが実装によっては2回走るか、1回だけ走るかが変わります。安全なデフォルトは2つ: cronをUTCで動かすか、01:00~04:00の窓を避けてスケジュールすることです。

クラウドのスケジューラは挙動が異なります。EventBridgeは常にUTC。Kubernetes CronJobsはv1.27で追加されたspec.timeZoneフィールドを尊重します。QuartzはJVMのデフォルトを使い、CronTriggerにタイムゾーンを渡さない限りそのままです。設定するスケジューラごとに、必ずドキュメントを一度読んでください。前のものの慣習がそのまま通用すると仮定しないこと。

発火しない式のデバッグ

10回中9回は式自体は問題なく、環境が間違っています。スケジュールを責める前にこのチェックリストをたどってください。

  • cronデーモンのログを読む: Debian/Ubuntuではgrep CRON /var/log/syslog、systemdホストではjournalctl -u cron。ログにはcronが実際に実行した解決済みコマンドラインが表示されます。
  • ユーザーを確認する。フラグなしのcrontab -lはあなた自身のcrontabを表示します。気になるスケジュールは/etc/crontab、/etc/cron.d/*、または別ユーザーのcrontabにあるかもしれません。
  • PATHを確認する。cronは最小限のPATH(通常/usr/bin:/bin)で動きます。絶対パスを使うか、crontabの先頭でPATH=を設定してください。
  • シェルを確認する。cronはログインシェルではなく/bin/shを使います。bash固有の機能(プロセス置換、[[ など)は明示的にbash -cでラップする必要があります。
  • 出力をリダイレクトする。> /var/log/myjob.log 2>&1 なしでは、失敗したジョブはローカルのrootにメールが届き、あなたは見ません。
  • タイムゾーンを確認する。ジョブ内でdate && date -uすると両方を記録できます。スケジュールが想定したものと一致しなければ、そこがバグです。
# より安全なcron行:
SHELL=/bin/bash
PATH=/usr/local/bin:/usr/bin:/bin
MAILTO=""

*/5 * * * *  /usr/local/bin/sync.sh >> /var/log/sync.log 2>&1

cronが向かない場面

cronは固定された時刻のスケジュールに優れ、単一ホスト上の1行ジョブには無敵です。一方で次の場合には適しません: 多数のマシン間での分散調整(キュー、リーダー選出付きスケジューラ、concurrencyPolicy: Forbid付きKubernetes CronJobsを使う)、失敗時のリトライとバックオフ(スクリプトをラップするかワークフローエンジンを使う)、分以下の粒度(systemdタイマーやイベント駆動トリガーを使う)、前回の実行が終わるのを待つ必要があるスケジュール(cronは前回がまだ走っていても平気で2つ目を起動します)。早めに限界を見極めると、深夜3時のページャーが大幅に減ります。

ビジュアルに作って検証する

1か月に2行以上cronを書くなら、各式をコミット前にパーサーで照合する習慣が最もコスパが高い投資です。Multilitiesは無料の/tools/cron-builderを提供し、5または6フィールドの式を平易な日本語にデコードし、次の10回の発火時刻をローカルタイムゾーンで表示します。これは日(月内)/曜日のORの罠と、*/7型ステップのサプライズを捕まえる、まさにそのクロスチェックです。既知のアンカー日付に対する次回発火時刻をアサートする小さな単体テストと組み合わせれば、スケジュールジョブは退屈なほど信頼できるものになります — それこそが望ましい姿です。

これらのツールを試す