五个字段是什么

标准 cron(Linux crontab)是五个字段,用空格分开:

分  时  日  月  周
*   *   *   *   *
字段 取值范围 说明
0-59
0-23 24 小时制
1-31 几号
1-12 或 JAN-DEC
0-7 0 和 7 都表示周日,或 SUN-SAT

特殊字符

字符 含义 例子
* 每一个取值都匹配 * * * * * 每分钟
, 列举 1,15 * * * * 每小时第 1 和第 15 分钟
- 范围 0 9-18 * * * 9 点到 18 点整点
/ 步长 */5 * * * * 每 5 分钟

20 个常用表达式

表达式 含义
* * * * * 每分钟
*/5 * * * * 每 5 分钟
0 * * * * 每小时整点
0 */2 * * * 每 2 小时
30 3 * * * 每天 3:30
0 0 * * * 每天 0 点(见下文说明)
0 9 * * * 每天上午 9 点
0 9 * * 1 每周一 9 点
0 9 * * 1-5 工作日 9 点
0 0 1 * * 每月 1 号 0 点
0 0 1 1 * 每年 1 月 1 日 0 点
0 0 */2 * * 每隔 2 天的 0 点
0 8,20 * * * 每天 8 点和 20 点
0 0-5 * * * 每天 0 点到 5 点的整点
*/10 9-18 * * 1-5 工作日 9-18 点每 10 分钟
15 14 1 * * 每月 1 号 14:15
0 22 * * 1-5 工作日 22 点
23 0-23/2 * * * 每天每隔 2 小时的 23 分
0 4 * * 0 每周日 4 点
0 0 1 */3 * 每季度首月 1 号

几个容易踩的坑

坑一:0 0 * * * 是每天 0 点,不是「0 点 0 分之外不执行」

读法是「分=0,时=0,日=任意,月=任意,周=任意」,也就是每天 00:00。

很多人第一次看到 * 会以为「任意」是「随便什么时候」,其实它的意思是这一位不限制

坑二:日期和星期不能同时指定

0 0 1 * 1 这种写法在不同实现里行为不同:

  • Vixie cron(Linux 默认):两个条件是的关系——每月 1 号每周一都执行
  • Quartz:会直接报错,必须把其中一个写成 ?

也就是说 0 0 1 * 1 在 Linux 上一个月会执行四五次,不是一次。这是个很隐蔽的坑。

想表达「每月 1 号且是周一才执行」,标准 cron 做不到,得在脚本里自己判断。

坑三:时区不是你的时间

cron 用的是服务器时区。如果服务器是 UTC 而你在东八区,那 0 2 * * *(你以为的凌晨 2 点)实际是北京时间上午 10 点。

排查「任务怎么没在预期时间跑」时,先确认服务器时区:

bashtimedatectl        # 或 date 看时区

坑四:环境变量不一样

cron 执行任务时的 PATH 和你在终端里不一样,很多命令会「找不到」。

解决办法:脚本里用绝对路径,或者在 crontab 开头显式声明环境变量:

bashPATH=/usr/local/bin:/usr/bin:/bin

坑五:前 5 分钟之外的秒级需求

标准 cron 最小粒度就是分钟,做不到「每 30 秒」。真有这个需求得用别的方式(比如脚本里循环 sleep,或者用 systemd timer)。

不同实现有差异

这是最容易出错的地方——同样是「cron 表达式」,不同框架的字段数不一样

实现 字段数 说明
Linux crontab 5 分 时 日 月 周
Spring @Scheduled 6 秒 分 时 日 月 周
Quartz 6 或 7 秒 分 时 日 月 周 [年]

从 Linux 拷表达式到 Spring 是高频事故:Linux 的 0 0 * * * 直接贴进 @Scheduled,会被当成「秒=0,分=0,时=*」,变成每小时执行一次而不是每天一次。

迁移时务必确认目标框架的字段数,别直接复制。

另外 Quartz 里「不指定」要用 ? 而不是 *(因为日期和星期互斥),这也是它和标准 cron 的显著差异。

生成和验证

写复杂表达式(带步长、多值、范围混合)时手写容易错。可以用 Cron 表达式生成器 勾选时间,它会同时给出 Linux 5 段、Spring 6 段、Quartz 7 段三种写法,并用人话解释下次执行时间——正好能避开上面「字段数不同」这个坑。

生成完建议再核对一遍下次执行时间的提示,这是验证表达式最直接的方式——Cron 表达式生成器 会把这个时间用人话写出来。