一、引言:为什么你需要这份大全?
在软件开发与系统运维中,定时任务是自动化不可或缺的一环。无论是每天凌晨备份数据库、每小时清理缓存、还是每5分钟检查服务状态,都需要一个可靠的任务调度机制。而cron
表达式,正是定义这种时间计划的标准语言。
然而,cron表达式看似简单(* * * * *
),其语法细节和特殊字符却常常让人困惑:
-
?
和*
在日期和星期字段有什么区别? -
如何实现“每月最后一个周五”?
-
Spring和Linux Crontab的表达式为何不完全兼容?
-
L
,W
,#
这些符号怎么用?
本文旨在提供一份最全面、最清晰、最实用的cron表达式参考大全,涵盖基础语法、海量示例、特殊用法、不同系统差异以及避坑指南,助你彻底掌握定时任务调度!
二、Cron表达式核心语法 (7字段标准)
一个完整的cron表达式通常由 7个时间字段组成(部分系统如Linux Crontab是5或6位,稍后解释),字段间用空格分隔。每个字段代表不同的时间单位。
字段顺序 | 字段含义 | 允许值范围 | 允许的特殊字符 | 必填 |
---|---|---|---|---|
1 | 秒(Seconds) | 0-59 | , - * / | 是 |
2 | 分(Minutes) | 0-59 | , - * / | 是 |
3 | 时(Hours) | 0-23 | , - * / | 是 |
4 | 日(Day of month) | 1-31 | , - * / ? L W | 是 |
5 | 月(Month) | 1-12 或 JAN-DEC | , - * / | 是 |
6 | 星期(Day of week) | 0-7 (0和7都代表周日) 或 SUN-SAT | , - * / ? L # | 是 |
7 | 年(Year - 可选) | 1970-2099 或 * | , - * / | 否 |
📌 重要说明
-
字段顺序: 必须严格按照
秒 分 时 日 月 星期 [年]
的顺序。 -
星期表示:
-
0
或7
= 星期日 (Sunday) -
1
= 星期一 (Monday) -
...
-
6
= 星期六 (Saturday) -
英文缩写 (
SUN
,MON
,TUE
,WED
,THU
,FRI
,SAT
) 通常也支持(取决于具体实现)。
-
-
年字段 (第7位):
-
在标准的 Unix/Linux Crontab 中通常没有年字段(只有5位:分 时 日 月 星期)。
-
在 Java生态(如Spring
@Scheduled
, Quartz)以及一些现代调度系统中通常支持年字段(7位)。 -
如果省略年字段(6位表达式),表示每年都执行。
-
-
日期(Day of month) 和 星期(Day of week) 字段的互斥性:
-
这两个字段在逻辑上存在一定冲突(一个指定月份中的哪一天,一个指定星期几)。
-
为了避免歧义,其中一个字段通常必须被设置为
?
(不指定),而另一个字段设置具体值。不能两个字段同时设置具体值(*
除外,它表示“每”)。 -
?
仅在 日 和 星期 字段有效。
-
三、特殊字符详解:解锁高级用法
字符 | 名称 | 作用 | 可用字段 | 示例说明 |
---|---|---|---|---|
* | 星号 (Any) | 所有值 / 每一。 | 所有字段 | * 在分钟字段 = 每分钟 |
? | 问号 (Blank) | 不指定值。仅在 日 和 星期 字段使用,解决它们之间的互斥问题。一个设置具体值,另一个就用? 。 | 日、星期 | 0 0 12 ? * MON = 每周一中午12点 |
- | 连字符 (Range) | 范围。 | 所有字段 | 10-15 在分钟字段 = 第10,11,12,13,14,15分钟 |
, | 逗号 (And) | 多个值。 | 所有字段 | MON,WED,FRI 在星期字段 = 周一、三、五 |
/ | 斜杠 (Step) | 间隔频率。A/B 表示从A开始,每隔B执行一次。 | 所有字段 | 0/15 在秒字段 = 第0,15,30,45秒*/5 * * * * ? = 每5秒 |
L | Last | 最后。不同字段含义不同: - 日字段: L = 当月最后一天- 星期字段: L = 周六 (7 或SAT )- 组合: L-3 = 倒数第几天 | 日、星期 | 0 0 L * ? = 每月最后一天午夜0 0 ? * 6L = 每月最后一个周五 (6 =周五, L =最后) |
W | Weekday | 最近工作日 (Nearest Weekday)。指定日期,如果该日期是周末,则调整到最近的工作日(周一-周五)。只能用于日字段。通常与具体日期组合使用。 | 日 (主要) | 15W 在日字段:- 如果15号是工作日 -> 15号 - 如果15号是周六 -> 14号(周五) - 如果15号是周日 -> 16号(周一) LW = 本月最后一个工作日 |
# | Number Sign | 第N个星期X。X#N 表示每月的第N个星期X。X =星期几(1-7 or SUN-SAT), N =第几个(1-5) | 星期 | 0 0 ? * 6#3 = 每月第3个星期五中午12点 (6 =周五, 3 =第三个) |
C | Calendar | 日历 (Calendar)。在Quartz Scheduler中引入,含义更复杂(如:5C 在日字段 = 日历中5号之后(包含5号)的第一天)。使用较少,且实现可能不一致。 | (Quartz扩展) 日、星期 | 非标准,建议谨慎使用 |
四、超级实用示例大全 (按执行频率分类)
🕒 1. 每秒 / 每分钟 / 每小时
表达式 | 含义 | 适用系统 |
---|---|---|
* * * * * ? * | 每秒执行一次 | Spring, Quartz |
*/1 * * * * ? * | 每秒执行一次 (同上) | Spring, Quartz |
0 * * * * ? * | 每分钟的第0秒执行 (每分钟一次) | Spring, Quartz |
0 */1 * * * ? * | 每分钟执行一次 (同上) | Spring, Quartz |
0 0 * * * ? * | 每小时的0分0秒执行 (每小时一次) | Spring, Quartz |
0 0 */1 * * ? * | 每小时执行一次 (同上) | Spring, Quartz |
0 0 * * * * | Linux Crontab 每小时执行 (注意无秒和年) | Linux Crontab |
📅 2. 每天 (特定时间)
表达式 | 含义 | 适用系统 |
---|---|---|
0 0 0 * * ? * | 每天凌晨00:00:00执行 | Spring, Quartz |
0 0 3 * * ? * | 每天凌晨03:00:00执行 | Spring, Quartz |
0 30 12 * * ? * | 每天中午12:30:00执行 | Spring, Quartz |
0 0 18 * * ? * | 每天下午18:00:00执行 | Spring, Quartz |
0 15 10 * * ? 2025 | 2025年每天上午10:15:00执行 | Spring, Quartz |
0 0 0,12 * * ? | 每天中午12点和午夜00点各执行一次 | Spring, Quartz |
0 0 9-17 * * ? | 每天上午9点到下午5点期间,每小时执行一次 (9,10,11,12,13,14,15,16,17) | Spring, Quartz |
0 0 9,13,17 * * ? | 每天上午9点、下午1点、下午5点各执行一次 | Spring, Quartz |
0 0 12 * * * | Linux Crontab 每天中午12:00执行 | Linux Crontab |
📆 3. 每周 (特定星期几)
表达式 | 含义 | 适用系统 |
---|---|---|
0 0 10 ? * MON * | 每周一上午10:00执行 | Spring, Quartz |
0 0 12 ? * MON-FRI * | 每周一至周五中午12:00执行 | Spring, Quartz |
0 0 9 ? * MON,WED,FRI * | 每周一、三、五上午09:00执行 | Spring, Quartz |
0 0 15 ? * 6 * | 每周六下午15:00执行 (6 =周六) | Spring, Quartz |
0 0 0 ? * 7 * | 每周日凌晨00:00执行 (7 =周日) | Spring, Quartz |
0 0 8 * * 1 | Linux Crontab 每周一上午8:00执行 (1 =周一) | Linux Crontab |
🗓️ 4. 每月 (特定日期)
表达式 | 含义 | 适用系统 |
---|---|---|
0 0 6 1 * ? * | 每月1号早上06:00执行 | Spring, Quartz |
0 0 20 15 * ? * | 每月15号晚上20:00执行 | Spring, Quartz |
0 0 12 5,15,25 * ? * | 每月5号、15号、25号中午12:00执行 | Spring, Quartz |
0 0 9 1-5 * ? * | 每月1号到5号,每天上午09:00执行 | Spring, Quartz |
0 0 0 L * ? * | 每月最后一天午夜00:00执行 | Spring, Quartz |
0 0 12 LW * ? * | 每月最后一个工作日中午12:00执行 | Spring, Quartz |
0 0 9 15W * ? * | 每月最接近15号的工作日上午09:00执行 (如果15号是周末,调整到临近工作日) | Spring, Quartz |
0 0 10 * * 1 | Linux Crontab 每月1号上午10:00执行 (注意星期字段是* ,Linux Crontab 中日期和星期都有效时会都触发,通常建议一个用* 一个用具体值或? ,但Linux Crontab不支持? ,所以需小心) | Linux Crontab (谨慎) |
🎯 5. 每月 (特定星期)
表达式 | 含义 | 适用系统 |
---|---|---|
0 0 10 ? * 6#2 * | 每月第2个星期五上午10:00执行 (6 =周五, #2 =第二个) | Spring, Quartz |
0 0 8 ? * 1#1 * | 每月第1个星期一上午08:00执行 (1 =周一, #1 =第一个) | Spring, Quartz |
0 0 15 ? * 7L * | 每月最后一个星期六下午15:00执行 (7 =周六, L =最后) | Spring, Quartz |
0 0 9 ? * 3L * | 每月最后一个星期三上午09:00执行 (3 =周三, L =最后) | Spring, Quartz |
🎄 6. 每年 (特定日期)
表达式 | 含义 | 适用系统 |
---|---|---|
0 0 0 1 1 ? * | 每年1月1日00:00执行 (元旦) | Spring, Quartz |
0 0 12 24 12 ? * | 每年12月24日中午12:00执行 (平安夜) | Spring, Quartz |
0 0 9 1 6 ? 2025 | 2025年6月1日上午09:00执行 | Spring, Quartz |
⏱️ 7. 复杂间隔与组合
表达式 | 含义 | 适用系统 |
---|---|---|
0 0/5 * * * ? * | 每5分钟执行一次 (在每分钟的0,5,10,15...55秒触发) | Spring, Quartz |
0 */10 * * * ? * | 每10分钟执行一次 (同上) | Spring, Quartz |
0 0 0/4 * * ? * | 每4小时执行一次 (0:00, 4:00, 8:00, 12:00, 16:00, 20:00) | Spring, Quartz |
0 0,30 9-17 * * MON-FRI * | 工作日(周一到周五)上午9点到下午5点期间,每小时0分和30分各执行一次 (如9:00, 9:30, 10:00, 10:30...17:00, 17:30) | Spring, Quartz |
0 15 10 L-2 * ? * | 每月倒数第2天上午10:15执行 | Spring, Quartz |
0 0 8 ? * MON#1 * | 每月第1个星期一上午8:00执行 | Spring, Quartz |
*/15 * * * * * | Linux Crontab 每15秒执行一次 (非标准实现,通常cron最小粒度是分钟,需要特殊技巧如多行或sleep) | Linux Crontab (非标准) |
0,30 * * * * * | Linux Crontab 每分钟的0秒和30秒执行 (相当于每30秒一次) | Linux Crontab (非标准) |
*/15 * * * * | Linux Crontab 每15分钟执行一次 (标准分钟级) | Linux Crontab |
五、不同系统差异与注意事项 (避坑指南)
-
字段数量差异:
-
Linux/Unix Crontab (5位): 标准格式为
分 时 日 月 星期
(无秒
和年
)。执行用户的环境变量(如PATH
)会影响命令查找,强烈建议脚本中使用绝对路径。 -
Linux/Unix Crontab (6位 - 部分系统): 有些实现(如某些cronie版本)支持6位,第一位是
秒
(秒 分 时 日 月 星期
),但仍然无年字段。非普遍支持,需确认系统版本。 -
Spring
@Scheduled
/ Quartz Scheduler (6位或7位): 通常支持6位 (秒 分 时 日 月 星期
) 或7位 (秒 分 时 日 月 星期 年
)。年
字段可选。这是Java生态中最常见的格式。
-
-
星期表示差异:
-
Linux Crontab: 通常
0
和7
都代表周日。1
=周一 ...6
=周六。 -
Spring/Quartz: 同样遵循
1
=周一 ...7
=周日 (0
有时也代表周日,但建议使用7
或SUN
以保证兼容性)。
-
-
特殊字符支持:
-
?
: 在Linux Crontab中通常不支持?
字符! 这是最大的坑。在Linux Crontab中,如果你想忽略“日”字段而指定“星期”字段,你只能把“日”字段设为*
,但这意味着“日”和“星期”字段都会生效(即满足任意一个条件就触发)。要精确指定星期几,通常需要依赖脚本逻辑或在表达式中小心处理(如* * * * 1
表示每月1号和每周一都触发)。Spring/Quartz 支持?
解决此问题。 -
L
,W
,#
: 这些通常不是标准POSIX cron的功能。它们主要存在于Quartz Scheduler及其衍生实现(如Spring Task)中。标准的Linux crontab (vixie-cron
,cronie
) 不支持L
,W
,#
。
-
-
环境变量:
-
Linux Crontab: Cron任务运行的环境不同于用户登录shell的环境!
PATH
,HOME
等变量可能非常精简。务必在脚本中设置所需环境变量或使用命令的绝对路径。错误日志通常会发送给系统用户(或配置的MAILTO用户)。
-
-
时区问题:
-
Cron表达式的执行时间依赖于运行cron服务的操作系统的时区设置。确保服务器时区配置正确(如
UTC
或所需的本地时区)。在Spring应用中,可以通过@Scheduled(cron="...", zone="Asia/Shanghai")
指定时区。
-
-
闰秒:
-
Cron通常不考虑闰秒。任务调度基于系统时钟,系统如何处理闰秒取决于操作系统和硬件。
-
-
启动与关闭:
-
修改Crontab文件后需要重启cron服务(如
sudo systemctl restart cron
或sudo service crond restart
)才能生效(部分系统会自动重载,但重启更保险)。 -
在Spring Boot应用中,修改
@Scheduled
的cron表达式通常需要重启应用。
-
-
权限问题:
-
编辑系统级crontab (
/etc/crontab
或/etc/cron.d/
下的文件)需要root
权限。 -
用户级crontab (
crontab -e
) 编辑的是当前用户的任务。 -
确保cron任务运行的用户有权限执行相应的命令或访问所需的文件。
-
-
资源消耗与并发:
-
非常高频(如每秒)的任务需谨慎,避免对系统造成过大负担。
-
注意任务执行时间可能重叠(如果前一个任务还没结束,下一个又开始了)。Spring的
@Scheduled
默认是单线程执行,任务会排队。Quartz可以配置线程池和并发策略。
-
六、工具推荐:验证与生成你的Cron表达式
不要手动硬猜!善用工具:
-
在线验证/生成器 (强烈推荐!):
-
IDE 插件:
-
IntelliJ IDEA / VSCode 等可能有cron表达式提示和验证插件。
-
-
代码中验证 (Spring/Quartz):
-
Spring的
CronExpression
类 (org.springframework.scheduling.support.CronExpression
) 可以用于解析和验证表达式有效性。 -
Quartz的
CronExpression
类 (org.quartz.CronExpression
) 同样提供验证功能。
-
七、总结
掌握cron表达式是自动化运维和开发的必备技能。理解其7字段结构、熟练掌握特殊字符(* ? , - / L W #
) 的含义、牢记不同系统(特别是Linux Crontab vs Spring/Quartz)的关键差异,并利用在线工具进行验证和生成,是写出正确、高效定时任务计划的关键。
希望这份大全能成为你案头的终极参考!收藏本文,定时任务无忧!