一、标准 5 段结构
Linux crontab 使用 5 个字段,用空格分隔,依次为:
分 时 日 月 周
* * * * *
│ │ │ │ │
│ │ │ │ └─ 星期 0-6(0=周日,部分实现 7 也=周日)
│ │ │ └───── 月 1-12
│ │ └───────── 日 1-31
│ └───────────── 时 0-23
└───────────────── 分 0-59
注意顺序是「分 时 日 月 周」,不是年月日时分秒。很多框架(Quartz、Spring 的 @Scheduled)用 6 或 7 段,多出来的通常是「秒」和「年」,别把 5 段写法直接抄到 6 段环境里。
二、特殊字符
*:任意值,该字段每个单位都触发。,:列举,如1,15,30表示第 1、15、30 分钟。-:范围,如9-18表示 9 点到 18 点每个小时。/:步长,*/5表示每 5 个单位一次;0 */2表示每 2 小时的第 0 分钟。?:不指定值,只用于「日」和「周」字段,主要出现在 Quartz 里,用来避免两者冲突;标准 crontab 不认识?。L、W、#:Quartz 扩展,分别表示最后一天、最近工作日、第几个星期几,crontab 不支持。
三、十个常用例子
0 2 * * * 每天凌晨 2:00
*/5 * * * * 每 5 分钟
0 9-18 * * 1-5 工作日 9:00-18:00 每小时
0 0 1 * * 每月 1 日 0:00
0 0 * * 0 每周日 0:00
30 3 * * 1 每周一 3:30
0 */2 * * * 每 2 小时整点
0 0 1,15 * * 每月 1 日和 15 日 0:00
0 12 * * 1-5 工作日中午 12:00
@reboot (crontab 简写)开机时执行一次
写「日」和「周」时要小心:如果两个字段都不为 *,多数实现是「或」的关系(任一满足就执行),这常导致「每月 1 日且周一」被误解为「每月 1 日或每周一」。想精确控制,建议只设其中一个字段,另一个留 *,或在脚本里再加判断。
四、怎么验证写得对不对
curl 一下在线解析工具,把表达式粘进去看中文释义和未来 5 次执行时间,比自己脑推可靠得多。本站的 Cron 解析工具 会把 5 段逐段翻译并推算出接下来的执行时刻,直接确认「每天 2 点」到底是不是你想要的那个 2 点(时区!)。
另外务必确认运行环境的时区:服务器是 UTC 而你想按北京时间跑,所有小时都要减 8;容器里没设 TZ 时尤其容易踩坑。crontab 还支持在文件顶部写 CRON_TZ=Asia/Shanghai。
五、crontab 之外的选择
systemd timer 用 OnCalendar,可读性更好、有日志和持久化;Quartz / Spring 支持秒级和年字段;云函数、K8s CronJob 各有自己的方言。表达式语义相近但细节不同,迁移时一定要按目标平台的文档核对,不要直接复制粘贴。
小结
记住 5 段顺序「分 时 日 月 周」,*/n 是步长,? 是 Quartz 专用,日与周同时指定时是「或」。写完用工具推算下一次执行时间,并核对时区,就能避免绝大多数定时任务事故。