crontab 的写法与速查表:5 个字段与符号的含义、常用示例和注册命令
“每天 3 点备份”“每 5 分钟运行一次监控脚本”这类定时任务,在 Linux 上只要在 crontab 里写一行就能设置。不过“分 时 日 月 星期”这 5 个字段与 * , - / 这些符号的组合,刚开始很容易看错,常会出现 1 小时运行 60 次、本想每 7 分钟一次却出现 4 分钟间隔之类的事故。本文以示例为主,讲解 crontab 写法的基础和符号含义、可直接套用的速查表、crontab -e、-l、-r 的用法,以及编写命令部分时的注意事项。
crontab 的一行就是“5 个字段 + 命令”
crontab 是告诉 cron 程序“什么时候运行什么”的配置文件。一行就是一项计划:开头是用空格分隔的 5 个表示时间的值,后面接要执行的命令。
| 顺序 | 字段 | 可写的值 | 示例 |
|---|---|---|---|
| 第 1 个 | 分 | 0-59 | 30 → 30 分 |
| 第 2 个 | 时 | 0-23(24 小时制) | 9 → 9 点,21 → 21 点 |
| 第 3 个 | 日 | 1-31 | 1 → 1 日 |
| 第 4 个 | 月 | 1-12(也可写 JAN-DEC) | 4 → 4 月 |
| 第 5 个 | 星期 | 0-7(0 和 7 都是周日,也可写 SUN-SAT) | 1 → 周一,5 → 周五 |
例如“30 3 * * * /home/user/backup.sh”表示“分为 30、时为 3、日、月、星期都不限”,也就是每天 3:30 执行 backup.sh。顺序是“从小单位到大单位”,但要注意只有星期排在最后。把时和分写反(想表示 9 点 30 分却写成“9 30”)是非常常见的错误,这样会变成“每天 30 点 9 分”,而时字段不能写 30,所以会报错。
符号的含义:记住 * , - / 这 4 个就能看懂
除了数字,cron 表达式还使用 4 个符号。它们在每个字段中的含义都一样,掌握这 4 个就能读懂几乎所有表达式。
| 符号 | 含义 | 示例 | 示例的读法 |
|---|---|---|---|
| * | 全部(该字段不做限定) | 0 9 * * * | 每天 9:00 |
| , | 列出多个值 | 0 9,18 * * * | 每天 9:00 和 18:00 |
| - | 范围(从……到……) | 0 9 * * 1-5 | 周一至周五 9:00 |
| / | 间隔(每隔) | */15 * * * * | 每小时 0、15、30、45 分 |
| - 与 / 组合 | 范围内的间隔 | 0 9-17/2 * * * | 9、11、13、15、17 点整 |
| , 与 - 组合 | 多个范围 | 0 9 * * 1-3,5 | 周一、二、三、五的 9:00 |
/ 的意思是“从该字段的第一个值开始每隔 n 计数”。“*/15”是“从 0 开始每隔 15”,即 0、15、30、45 分;“5-59/15”则是 5、20、35、50 分。它不是“过了 15 分钟就运行下一次”的计时器,而是“在这些分钟值运行”的指定。理解了这一点,就能避开后面一节的陷阱。
另外还有可以代替 5 个字段的简写:@daily(每天 0:00)、@hourly(每小时 0 分)、@weekly(周日 0:00)、@monthly(每月 1 日 0:00)、@yearly(1 月 1 日 0:00)、@reboot(启动时运行一次)。它们易读,但时间固定在 0:00,要注意容易和半夜 0 点的其他处理撞在一起。
可直接套用的 crontab 速查表
下面列出常用的指定方式。时间均为服务器时区的时间。命令部分已省略,请在表达式后面接上要执行的命令。
| 想做的事 | cron 表达式 | 补充 |
|---|---|---|
| 每 1 分钟 | * * * * * | 每天运行 1,440 次 |
| 每 5 分钟 | */5 * * * * | 每小时 0、5、10……55 分 |
| 每 10 分钟 | */10 * * * * | 每小时 0、10、20……50 分 |
| 每 30 分钟 | */30 * * * * | 每小时 0 分和 30 分 |
| 每小时(整点) | 0 * * * * | 与 @hourly 相同 |
| 每 2 小时 | 0 */2 * * * | 0、2、4……22 点 |
| 每 6 小时 | 0 */6 * * * | 0、6、12、18 点 |
| 每天 0:00 | 0 0 * * * | 与 @daily 相同 |
| 每天 3:30(深夜批处理) | 30 3 * * * | 避开 0 点整不容易拥挤 |
| 每天 9:00 和 18:00 | 0 9,18 * * * | 分钟相同的时间可以合并 |
| 工作日(周一至周五)9:00 | 0 9 * * 1-5 | |
| 工作日 9 点到 18 点每 30 分钟 | */30 9-18 * * 1-5 | 18:30 也会运行 |
| 周六日 10:00 | 0 10 * * 0,6 | 周日写 0 或 7 都可以 |
| 每周一 9:00 | 0 9 * * 1 | |
| 每周五 17:30 | 30 17 * * 5 | |
| 每月 1 日 0:00 | 0 0 1 * * | 与 @monthly 相同 |
| 每月 1 日和 15 日 9:00 | 0 9 1,15 * * | |
| 每季度第一天(1、4、7、10 月 1 日) | 0 0 1 */3 * | |
| 每年 1 月 1 日 0:00 | 0 0 1 1 * | 与 @yearly 相同 |
| 启动时运行一次 | @reboot | 仅 crontab(Linux)的写法 |
像“每月最后一天”“每月第 2 个周二”“最后一个周五”这样,有些指定只靠 crontab 的 5 个字段无法准确写出。这些需要在命令一侧判断日期,详见cron 日期与星期的 OR 规则及第 n 个星期几、月末的写法。
*/7 不是每 7 分钟:间隔指定的陷阱
“*/n”的意思是“从 0 开始每隔 n 计数,超过该字段的最大值就停止”。分钟字段在 0-59 内计数,所以如果 n 不能整除 60,间隔就会在每小时 0 分处被打乱。
| 表达式 | 实际运行的时间 | 被打乱的位置 |
|---|---|---|
| */7 * * * * | 每小时 0、7、14、21、28、35、42、49、56 分 | 56 分 → 下一次 0 分只隔 4 分钟 |
| */25 * * * * | 每小时 0、25、50 分 | 50 分 → 下一次 0 分只隔 10 分钟 |
| */45 * * * * | 每小时 0、45 分 | 45 分 → 下一次 0 分只隔 15 分钟 |
| 0 */5 * * * | 0、5、10、15、20 点 | 20 点 → 次日 0 点只隔 4 小时 |
| 0 */7 * * * | 0、7、14、21 点 | 21 点 → 次日 0 点只隔 3 小时 |
只要是能整除 60 或 24 的数,间隔就会均匀:分钟可用 1、2、3、4、5、6、10、12、15、20、30,小时可用 1、2、3、4、6、8、12。如果一定要用其他间隔均匀运行,就让 cron 每分钟或每小时运行一次,再在脚本中根据距上次运行的时间决定是否执行。
crontab -e、-l、-r 的用法,以及避免误删的注意事项
crontab 的设置不是直接打开文件,而是用 crontab 命令编辑。每个用户都有各自的 crontab,命令操作的是执行该命令的用户的设置。
- 1
用 crontab -l 确认当前设置
会显示所有已注册的行。如果什么都没注册,会显示“no crontab for 用户名”。编辑前先用 crontab -l > ~/crontab.bak 备份一下会更放心。
- 2
用 crontab -e 编辑
会打开编辑器,每行写一项计划后保存退出。保存时就会反映到 cron,无需重启 cron。如果格式有误,保存时会报错并询问是否重新编辑。
- 3
最后一行之后要换行
有些 cron 实现中,最后一行没有换行就不会被读取。养成在最后一行之后按一下回车再保存的习惯吧。
- 4
root 或其他用户的设置用 sudo crontab -u
像 sudo crontab -u www-data -l 这样,用 -u 指定目标用户。以哪个用户注册,会影响文件权限和可用的环境变量。
另外,/etc/crontab 和 /etc/cron.d/ 下的文件格式稍有不同,在 5 个字段和命令之间多一个“执行用户名”字段(例:30 3 * * * root /usr/local/bin/backup.sh)。如果按用户 crontab 的写法复制过去,命令名会被当作用户名读取,导致无法运行。
编写命令部分时的 4 个注意事项
即使时间的 5 个字段都正确,也常因命令的写法导致“手动执行能运行,cron 却不行”。这是因为 cron 会在与登录时不同的最小化环境中执行命令。
- 命令和文件要写绝对路径。cron 的 PATH 只有 /usr/bin:/bin 左右,放在 /usr/local/bin 等位置的命令会找不到
- % 要转义。crontab 命令中的 % 会被当作换行,所以 date +%Y%m%d 要写成 date +\%Y\%m\%d
- 把输出留在日志里。在末尾加上“>> /var/log/backup.log 2>&1”,就能连同错误一起追加记录
- shell 是 /bin/sh。如果要用 bash 专用的写法([[ ]] 或数组等),就写成 bash 脚本并在开头写 #!/bin/bash
综合以上几点,例如“工作日 9:00 以带日期的文件名输出报告”的设置就是“0 9 * * 1-5 /usr/local/bin/report.sh > /home/user/report-$(date +\%Y\%m\%d).txt 2>&1”。如果仍然不运行,可以按照cron 不运行的原因检查清单依次排查。
要确认写好的表达式是否符合意图,最方便的是把 crontab 的那一行直接粘贴到 cron 表达式检查器中。它会翻译成“工作日(周一至周五)9:00”这样的说明,并列出接下来 10 次的执行时间,星期或时间弄错能马上发现。命令部分忘记转义 % 也会提示。
常见问题
- crontab 中每 5 分钟运行一次怎么写?
- 写成“*/5 * * * * 命令”。会在每小时 0、5、10……55 分运行,1 小时 12 次,1 天 288 次。如果只想在工作日 9 点到 18 点运行,就用时和星期字段限定,如“*/5 9-18 * * 1-5”。
- crontab 的星期数字中哪个是周一?
- 0 是周日,1 是周一,2 是周二……6 是周六,7 也视为周日。工作日(周一至周五)是 1-5,周六日是 0,6。也可以用 MON、FRI 这样的 3 个英文字母书写。注意 AWS EventBridge 中 1 是周日,相差一位。
- 编辑 crontab 后需要重启 cron 吗?
- 用 crontab -e 编辑时,保存后会自动生效,无需重启。修改了服务器的时区时,cron 可能仍按旧时区运行,这时要重启 cron(或 crond)服务。
- 用 crontab -r 删除的设置能恢复吗?
- crontab -r 会不经确认删除设置,没有撤销功能。如果有备份(保存了 crontab -l 输出的文件),可以用 crontab 文件名 写回。没有备份的话,只能以 cron 日志(syslog 等)中留下的执行记录为线索,根据哪个命令在什么时间运行过来重新编写。
- 为什么“*/7”不是每 7 分钟一次?
- */7 的意思是“从 0 分开始每隔 7 计数到 59 分”,会在 0、7、14……56 分运行。56 分之后不是 63 分,而是下一小时的 0 分,所以只有这里的间隔是 4 分钟。使用能整除 60 的 5、10、15、20、30 等,间隔就会均匀。
本文介绍的工具
相关文章
让 GitHub Actions 的 cron 按日本时间运行:UTC 换算速查表与星期偏移、延迟的对策
在 GitHub Actions 的 schedule 里写了“0 9 * * 1-5”,工作流却在日本时间傍晚 6 点才运行。这并不是写错了,而是因为 GitHub Actions 的 cron 一律按 UTC(协调世界时)解读。本文整理了把日本时间(JST)换算成 UTC cron 表达式的方法和速查表、指定早上 0 点到 8 点时出现的“星期和日期退回前一天”问题、一行写不下时如何拆成两行,以及 schedule 延迟或不运行的原因与对策。
阅读文章 →cron 的日期与星期是 OR 关系:第 2 个周一、月末、最后一个周五、隔周的正确写法
想表示“每月第 1 个周一 9 点”而写成“0 9 1-7 * 1”,结果除了每周一之外,每月 1 到 7 日每天都运行了。cron 中这个非常常见的失误,源于 cron 特有的规则:同时指定日期和星期时,只要符合“其中之一”就会运行。本文结合实际执行日期的示例,讲解日期与星期构成 OR 的机制及其例外,在 crontab 中正确运行第 n 个星期几、月末、最后一个周五、隔周的写法,以及 AWS EventBridge 和 Spring 中可用的 L、#、W 的写法。
阅读文章 →EventBridge cron 表达式的写法:? 的含义、星期数字、日本时间的指定与从 crontab 改写
在 AWS 上定时运行 Lambda 或 ECS 任务时使用的 EventBridge cron 表达式,看起来和 Linux 的 crontab 很像,但字段数量、? 的用法、星期数字和时区都略有不同。按 crontab 的习惯写成“0 9 * * 1-5”会报错,直接照搬星期数字会差一天,时间按 UTC 解读又会差 9 小时,绊脚的地方接二连三。本文整理了 EventBridge cron 表达式的 6 个字段与 ? 的规则、星期数字、L、W、# 的用法、与 rate 表达式的区别、规则和 Scheduler 中日本时间的处理,以及与 crontab、Kubernetes、Spring、systemd 的改写对照表。
阅读文章 →