「每天早上 9 点跑一次报表」「每周一凌晨清理日志」——这些需求写成 Cron 表达式只需要 5 个字段,但符号规则不熟悉时,写出来的任务不是不跑就是狂跑。这篇按字段拆开讲,配 12 个直接可用的实例。
| 位置 | 含义 | 取值范围 |
|---|---|---|
| 第 1 位 | 分钟 | 0–59 |
| 第 2 位 | 小时 | 0–23 |
| 第 3 位 | 日 | 1–31 |
| 第 4 位 | 月 | 1–12 |
| 第 5 位 | 星期 | 0–7(0 和 7 都是周日) |
符号规则:* 任意值;, 枚举;- 区间;/ 步长(*/15 在分钟位 = 每 15 分钟)。0 9 * * 1-5 读作「周一到周五,每天 9:00」——这就是最经典的工作日早晨定时。
*/5 * * * * — 每 5 分钟(监控探活);0 * * * * — 每小时整点(数据同步);30 8 * * * — 每天 8:30(晨报推送);0 9 * * 1-5 — 工作日 9:00(站会提醒);0 2 * * * — 每天凌晨 2:00(数据库备份);0 3 * * 0 — 每周日凌晨 3:00(全量清理);0 0 1 * * — 每月 1 号零点(账单结算);0 10 15 * * — 每月 15 号 10:00(月度中检查);0 0,12 * * * — 每天 0 点和 12 点各一次(双高峰任务);0 9-18 * * * — 每天 9 点到 18 点的每个整点(工作时段巡检);0 0 * * 1,4 — 每周一和周四零点(双周任务日的合写);59 23 28-31 * * — 每月最后几天末尾跑一次,脚本内自判是否为月末(Cron 本身难表达「月末」,这是惯用变通)。0 9 * * * 实际在北京时间 17:00 跑。要么改系统时区,要么按 UTC 换算后再写;* 时是「或」关系(1 号或周一都会跑),而不是直觉上的「且」。不确定就只用其中一个字段,另一个保持 *;13、17、23 这类错开的分钟数排布,避免全站任务挤在同一分钟造成 IO 尖峰,也避免互相抢锁;另外记住一个排障利器:系统日志里记录了每次触发时间(Ubuntu 在 /var/log/syslog,搜 CRON)。任务「没跑」还是「跑了但失败」,先查这里再改代码。
问:为什么有 5 字段和 6 字段两种说法?
经典 Unix Cron 是 5 字段;Quartz(Java 生态)等用 6 字段,第 1 位是秒,且「星期」从 1 开始。跨平台复制表达式前先确认格式。
问:想表达「工作日 9 点到 18 点每半小时」?*/30 9-18 * * 1-5——分钟位步长 30,小时位限 9–18,周位限工作日。