悦省yueser.com

← 全部指南

cron 表达式指南:五段含义与下次执行

把表达式粘进来,看它接下来几次跑在什么时间,比对着文档数星号快得多。上线前先跑一遍,能挡掉大部分「过了几天才发现没执行」的问题。

打开工具:Cron 表达式

什么时候需要先解析一遍

接手别人的 crontab 时最难判断的是意图:0 3 * * 1-5 到底是每天 3 点还是工作日 3 点,得算一遍才敢改。自己写的时候也一样,尤其是带 */n、区间和列表混用的表达式,光看很难确认边界对不对。

还有一种排查场景:任务「有时候跑有时候不跑」。原因常常藏在表达式里——月份和日期同时限制导致某些月直接跳过、时区差了 8 小时、或者 2 月没有 31 号。把未来几次执行时间列出来,这类问题一眼就能看出来。

五段怎么读,哪里最容易错

五段依次是:分钟 0-59、小时 0-23、日 1-31、月 1-12、星期 0-7(0 和 7 都表示周日)。支持星号任意、*/n 步长、a-b 区间、a,b 列表,也能组合成 1-5,0。注意 */n 的意思是「从最小值开始每隔 n 个取一个」,也就是取能被 n 整除的值,而不是「每 n 个单位执行一次」。在分钟段上两种理解结果一样,但在 1-31 的日段上,*/2 取的是奇数天。

日和星期是最容易被误解的一对。在 Vixie cron(绝大多数 Linux 发行版用的实现)里,当这两段都被限制、都不是星号时,它们是「或」的关系:0 0 1 * 1 会在每月 1 号执行,也会在每个周一执行,而不是「既是 1 号又是周一」。想表达「且」,只能把其中一段改回星号,或者把判断写进脚本。

局限:cron 只负责按时启动,不负责跑完

cron 不补跑。机器在 3 点关机、3 点半才开机,那一次就丢了,开机后不会补上。任务执行时间超过间隔时,cron 也不会等你,会再起一个进程——两个实例同时改同一份数据,事故往往出在这里。长任务前面建议加 flock,或者换成带锁的任务队列。

另外,这个工具只解析标准五段式,不支持 @daily、@reboot 这类宏,也不支持秒级字段和 Quartz 的 L、W、# 语法(那些是六段或七段式)。需要秒级精度、错过要补跑、或者要看执行历史,应该用 systemd timer、任务队列或调度服务。

常见问题

cron 里日和星期同时限制是什么意思?

在标准 cron(Vixie 实现)里,两段都不是星号时按「或」处理:满足任意一个就执行。0 0 1 * 1 表示每月 1 号以及每个周一,不是「每月 1 号且是周一」。要表达「且」,只能把一段改成星号,或者让任务每天都跑、在脚本里判断日期。

cron 任务没按预期执行,怎么排查?

按顺序看四件事:表达式是否被理解成了你以为的时间(用这里的下次执行时间对照)、服务器时区是不是你想的时区、任务环境的 PATH 和当前目录与登录 shell 不同所以命令要用绝对路径、以及 cron 日志(/var/log/cron 或 journalctl -u cron)里有没有执行记录。

为什么 2 月 31 号的任务不会执行?

因为 2 月没有 31 号。日段写 31 时,只有 1、3、5、7、8、10、12 月会触发,其余月份直接跳过,而且不会报错。月底任务建议写成 28 号,或者在脚本里判断「今天是不是本月最后一天」。

相关工具:Cron 表达式