Cron 定时任务速查表 - Cron 定时任务配置命令大全
定时任务的语法只有五个字段加三个符号,十分钟就能学会;真正让人卡住的是它跑起来之后的环境和归属。cron 的执行环境不是你的终端环境:PATH 通常只有三条目录,shell 里 alias 和 pyenv、nvm 带来的路径一个都没有,于是任务"没报错但没效果"或干脆 command not found。第二个坑是用户:放进 /etc/cron.d/ 的按文件属主跑,放进 crontab 的按该用户跑,同一条命令换位置结果就不同。这张表把时间格式与特殊字符串(@daily、@reboot)、系统级目录、用户级 crontab 命令、环境变量、日志重定向和排查顺序归在一起。什么时候该翻它:任务"确认被调用了但产物没有",先 grep syslog 确认调度发生,再 systemctl status cron 确认守护进程,两处都空说明 crond 没起
典型使用场景
面向必须让某件事「在没人盯着的时候也发生」的运维与后端工程师,具体是四类场景。一是备份与清理:凌晨 2 点跑 backup.sh、每月 1 号跑 cleanup.sh、日志交给 logrotate。二是健康探测与告警:*/5 * * * * curl -s https://example.com/health,比外部拨测系统更早发现本机进程挂掉。三是业务节律:工作日 9 点发日报(0 9 * * 1-5)、每周日 0 点出周报(0 0 * * 0),这类任务最容易被时区和「周日的 0 与 7」弄错。四是自愈:@reboot 在机器重启后拉起常驻进程,替代裸写 rc.local。真正掌握 cron 的关键不在记格式,而在三件事:cron 的环境变量极简(没有 PATH、没有你的 shell 交互配置),输出默认走 MAILTO 所以「脚本没跑但你没收到任何东西」是常态,时区取决于系统 TZ 而不是你的本地设置。
时间格式与特殊字符 7
* * * * * command*/15 * * * *0 */2 * * *0 18 * * 0-610 2 * * 6,70 0 * * 00 0 1 * *特殊字符串 6
@reboot@hourly@daily / @midnight@weekly@monthly@yearly / @annually系统级 cron 6
/etc/crontab/etc/cron.d//etc/cron.hourly//etc/cron.daily//etc/cron.weekly//etc/cron.monthly/用户级 crontab 命令 7
crontab -ecrontab -lcrontab -rcrontab -ricrontab -u user -ecrontab -u user -lecho "@reboot echo hi" | crontab环境变量配置 6
SHELL=/bin/bashPATH=/usr/local/bin:/usr/bin:/binMAILTO="admin@example.com"MAILTO=""HOME=/rootCRON_TZ=Asia/Shanghai常用示例 6
0 2 * * * /path/backup.sh*/5 * * * * curl -s https://example.com/health0 9 * * 1-5 mail -s "Report" team@example.com0 0 1 * * /path/cleanup.sh30 3 * * * /usr/bin/logrotate /etc/logrotate.conf0 0 * * 0 /path/weekly-report.sh >> /var/log/report.log 2>&1调试与排查 5
grep CRON /var/log/sysloggrep cron /var/log/crondatesystemctl status cron* * * * * echo test >> /tmp/cron-test 2>&1参数矩阵
| 参数 | 作用 | 示例 |
|---|---|---|
* * * * * | 五个字段分别是 分 时 日 月 周,每分钟执行一次,是验证 cron 是否生效的标准探针 | * * * * * command |
*/15 * * * * | 步长语法,分字段可用;「分」用 */15 表示每 15 分钟,比写四条 */5 更不易漏 | */15 * * * * |
0 */2 * * * | 小时字段用步长表示每 2 小时一次;注意这是 0,2,4…22 点,不是「每两小时从当前时刻起算」 | 0 */2 * * * |
0 18 * * 0-6 | 用范围列举多天,逗号隔开;字段取值为 0-6(周日可用 0 或 7) | 0 18 * * 0-6 |
10 2 * * 6,7 | 逗号列举多个取值,周六周日的 02:10;比写两条规则更不容易出现重复或漏跑 | 10 2 * * 6,7 |
0 0 1 * * | 「日」字段取 1,表示每月 1 号零点;月度任务的常用写法 | 0 0 1 * * |
@reboot | 开机后执行一次,替代 rc.local,是最省事的「重启后自动拉起」方案 | @reboot |
@hourly / @daily / @weekly / @monthly | 预设宏,分别等价于 0 * * * *、0 0 * * *、0 0 * * 0、0 0 1 * *;@midnight/@annually 是同义别名 | @daily |
SHELL=/bin/bash | 指定执行命令所用的 shell;不写就用 /bin/sh,而 sh 不认 source、[[ ]]、pipefail | SHELL=/bin/bash |
PATH=/usr/local/bin:/usr/bin:/bin | cron 的 PATH 极简,不含 nvm、pyenv、homebrew 路径,node/python 等命令常因此 not found | PATH=/usr/local/bin:/usr/bin:/bin |
CRON_TZ=Asia/Shanghai | 为单个 crontab 指定时区,优先于系统 TZ;跨时区团队里这是唯一可靠做法 | CRON_TZ=Asia/Shanghai |
MAILTO="" | 把 cron 的输出投递清空,避免每次执行都往邮箱发一封空邮件 | MAILTO="" |
crontab -e / crontab -l | 编辑与列出当前用户的 crontab;改完必须 crontab -e 保存才会被重新加载 | crontab -e |
crontab -r / crontab -ri | -r 直接删除全部 crontab 且不确认,-ri 才会交互确认;误用 -r 的后果无法撤销 | crontab -ri |
crontab -u user -e / -l | 以 root 身份管理其他用户的 crontab,vip 与 sudo 用户的任务只能这样改 | crontab -u user -l |
/etc/crontab 与 /etc/cron.d/ | 系统级位置;/etc/cron.d/ 下的文件必须以非登录用户为属主且权限 644,且需要用户名字段 | /etc/crontab |
>> /var/log/report.log 2>&1 | 把标准输出与错误输出重定向进文件,是排查「cron 到底跑了没」最有效的手段 | 0 0 * * 0 /path/weekly-report.sh >> /var/log/report.log 2>&1 |
易错点与避坑指南
现象crontab -e 里写好的任务手动执行正常,到点却完全不跑,且没有任何报错。
原因五字段写错位置(比如把月份写在最前面)或分隔符用了多个空格/制表符混排,cron 静默忽略整行;也可能是环境差异——交互 shell 有 PATH 而 cron 没有。
处置先加一行 * * * * * echo test >> /tmp/cron-test 2>&1 验证调度器本身,通了再排查业务行;同时在 crontab 顶部显式写 SHELL 与 PATH。
现象在 macOS 上设的凌晨 2 点任务,实际在下午某个时间才执行。
原因cron 按系统本地时区解释字段,笔记本/服务器时区与预期不一致;macOS 上 Laptop 睡眠期间任务会被推迟到唤醒后补跑。
处置用 date 确认系统时区,并在 crontab 里加 CRON_TZ=Asia/Shanghai 显式声明,与 date 输出对照确认。
现象以为 0 0 * * 7 是「每周日」,结果周日的任务和周日的其它任务跑重了或根本没跑。
原因cron 的周字段 0 与 7 都表示周日,写 7 与写 0 等价;更常见的是把「周日」误解成 1(那是周一)。
处置统一用 0 表示周日,写 0 0 * * 0,需要周六周日就写 0 0 * * 6,0。
现象脚本里调用 node / python / gcloud 报 command not found,而终端里同样的命令没问题。
原因cron 的 PATH 只有 /usr/bin:/bin,nvm、pyenv、homebrew、asdf 装出来的二进制都不在其中。
处置在 crontab 顶部写全 PATH,或直接在规则里调用绝对路径(例如 /usr/local/bin/node),最稳的是包一层 . /path/to/env.sh。
现象用 crontab -r 清理测试任务,第二天发现整份生产 crontab 全没了。
原因crontab -r 不做任何确认,直接删除当前用户的全部任务,且不保留备份文件。
处置养成用 crontab -ri 的习惯;重要任务用 crontab -l > ~/crontab.bak 先备份,再做增删。
现象把日志清理任务写进 crontab 后日志没被清,反而生成了一堆 0 字节文件。
原因logrotate 依赖 /etc/logrotate.conf 里的状态文件与运行用户,直接在 cron 里手工调 logrotate 会绕过轮转判断,重复轮转或路径写错。
处置交给 logrotate 自身的 timer/cron 机制,规则里用 30 3 * * * /usr/bin/logrotate /etc/logrotate.conf 并确保脚本内 exit 0。
现象在 /etc/cron.d/ 下新建的文件不执行。
原因/etc/cron.d/ 要求文件属主必须是 root 之外的用户、权限 644,且每行末尾需要写运行用户字段;权限 600 或漏写用户都会让 cron 拒绝该文件。
处置chmod 644 /etc/cron.d/myjob 并 chown root:root 之外的用户名规范,同时在命令后补上用户列(如 * * * * * deploy /path/x.sh)。
现象两个任务几乎同时启动,互相抢占同一个文件/端口,偶尔其中一个失败。
原因相邻的时间字段让两个任务在同一分钟内并发执行,缺少互斥机制。
处置错开分钟字段(如 0 2 与 5 2),或在脚本开头用 flock 排他执行:flock -n /var/lock/backup.lock /path/backup.sh。
排障路径
1先用一分钟探针确认调度器本身活着
* * * * * echo test >> /tmp/cron-test 2>&1一分钟后看 /tmp/cron-test 有没有新行;没有就查 daemon 状态,而不是查业务脚本。
2确认 cron 服务在跑
systemctl status cronDebian/Ubuntu 单元名是 cron,RHEL/CentOS 是 crond;未启动则任务不会解析。
3查系统日志里 cron 的加载与执行记录
grep CRON /var/log/syslog能看到 CRON 的调度记录说明规则已被解析;完全无记录说明规则语法被拒或文件未被读取。
4RHEL/CentOS 系日志路径
grep cron /var/log/cron同一批信息在 RHEL 系写在这个文件,而不是 /var/log/syslog。
5确认机器当前时区与期望时区一致
datecron 按系统本地时区执行;与 CRON_TZ=Asia/Shanghai 声明不符说明任务时间整体偏移。
6列出并备份当前用户的 crontab
crontab -l输出为空说明当前用户根本没有任务;改之前先 crontab -l > ~/crontab.bak 留底。
命令示例
带日志与错误重定向的备份任务
0 2 * * * /path/backup.sh >> /var/log/backup.log 2>&1
crontab -lcron 的环境极其精简,脚本里 PATH、HOME、语言变量都可能缺失,能用绝对路径就全部写绝对路径。>> 追加、2>&1 把 stderr 并入日志——不重定向的话失败只会静默发生,你只会看到「任务没跑」。
先写调试任务验证 cron 真的在跑
* * * * * echo test >> /tmp/cron-test 2>&1
sleep 65
cat /tmp/cron-test
rm -f /tmp/cron-test排查「任务不执行」的标准第一步:先用一个每分钟写文件的最小任务区分是「cron 服务没跑」还是「你的命令本身有问题」。这一条过了再去查业务脚本,能省掉大量在错误方向上的排查。
输出
test
PATH / SHELL / CRON_TZ 写在 crontab 顶部
SHELL=/bin/bash
PATH=/usr/local/bin:/usr/bin:/bin
CRON_TZ=Asia/Shanghai
MAILTO=""cron 的默认 PATH 常常只有 /bin:/usr/bin,自己装的 go、node、python 全部找不到——这是「手动跑正常,cron 里 command not found」的标准原因。CRON_TZ 让跨时区团队的任务不必去改服务器时区;MAILTO="" 直接关掉邮件。
输出
* * * * * /usr/bin/logrotate /etc/logrotate.conf
用管道快速设置任务
echo "@reboot /path/start-app.sh" | crontab
crontab -lcrontab 命令从 stdin 读新内容,所以 echo | crontab 会整体覆盖现有任务。适合脚本化初始化,但先 crontab -l 备份一份——这是最容易在自动化脚本里丢掉全部任务的操作。
在 /etc/cron.d 放独立任务文件
SHELL=/bin/bash
PATH=/usr/local/bin:/usr/bin:/bin
0 3 * * * root /usr/local/bin/nightly.sh >> /var/log/nightly.log 2>&1/etc/cron.d/ 里的文件格式比用户 crontab 多一列用户名,漏掉这一列该文件会被 cron 直接忽略,且不报错。每个任务独立成文件、带 .run 之类的非标准后缀可避免被 cron 主动执行,权限也必须是 644 且属主 root。
常见坑与注意事项
- crontab 里的
%不是普通字符,而是「换行」转义符。未转义的%会截断命令行导致任务静默失败——日期格式必须写成 date +\%Y-\%m-\%d,路径含 % 的同样要转义。 - crontab -r 直接删除当前用户的全部定时任务,没有任何确认和回收站。用 crontab -ri 才会先问一次;自动化脚本里更该用 crontab -l > backup.txt 留底。
- 命令里的 % 必须转义,而命令里写 redirect 与 && 在部分 cron 实现下行为不一致。可靠写法是全部包进脚本文件,crontab 里只留一行绝对路径调用。
- 本表只讲 crontab(定时任务);页面里若同时出现「cron 与 crontab 两表」,务必先分清:cron 是守护进程(systemctl status cron),crontab 是编辑用户计划任务的命令,二者不可混用。
- cron 按服务器时区执行,容器、虚拟机和夏令时切换都可能让任务提前或推迟一小时。用 CRON_TZ 显式指定时区,或先 date 确认系统时区。
- 系统重启后错过的任务不会补跑(除非用 anacron 类机制);同时 MAILTO 未关闭时,任务里每一条 stdout 都会变成一封邮件,几百行日志会塞爆邮箱或触发退信。
提示
- 时间格式:分(0-59) 时(0-23) 日(1-31) 月(1-12) 周(0-6, 0=周日)。
- * 表示所有值,, 分隔多个值,- 表示范围,/ 表示步长。
- 脚本路径必须用绝对路径,否则可能找不到命令。
- cron 环境变量 minimal,建议在脚本里显式设置 PATH。
- 日志输出:command >> /var/log/cron.log 2>&1 重定向 stdout 和 stderr。
- 周日和周天都可用 0 或 7 表示,不同发行版可能不同,建议统一用 0。
CRON_TZ=Asia/Shanghai让任务按指定时区触发,服务器跑在 UTC 上时不必再换算时间。crontab -r直接删掉整个 crontab 且不留备份,要删单条请用crontab -e逐行删。@reboot只在开机瞬间跑一次,开机自启的服务交给 systemd 单元更可靠且有日志。
常见问题
写进 crontab 的任务到点完全不执行,先查什么?
按这个顺序排查最省时。第一步确认服务本身活着:systemctl status cron 看 cron 守护进程是否 running。第二步确认任务确实写进去了:crontab -l 列出当前用户的所有任务,空输出说明根本没保存上(用 crontab -e 时编辑器里写完没退出保存同样会丢)。第三步看调度器有没有触发:Debian/Ubuntu 用 grep CRON /var/log/syslog,RHEL/CentOS 用 grep cron /var/log/cron,能看到具体哪一分钟调起了哪条命令。第四步用 date 确认服务器时区,因为 cron 按系统时区执行,跟你本地时间不一致时就会以为「没跑」。
为什么任务手动执行没问题,放进 cron 就失败?
根因是 cron 的环境极为精简:它不是登录 shell,不加载你的 .bashrc 或 .zshrc,PATH 往往只剩几个系统目录,HOME、SHELL 也可能不是你预期的值。结果是脚本里调 node、python、curl、git 这类非系统目录下的命令时找不到,或者找不到依赖、环境变量为空。两条修法:一是把命令和脚本全部写成绝对路径;二是在 crontab 顶部显式声明环境,SHELL=/bin/bash、PATH=/usr/local/bin:/usr/bin:/bin、HOME=/root,日志排查时把 MAILTO 设为空可关掉邮件通知,避免错误信息被吞掉。
任务跑了但没有任何输出,怎么留住现场?
cron 默认把任务的输出发到本地邮箱,看不到就得显式重定向。常用组合是 >> /var/log/cron.log 2>&1:>> 追加而不是覆盖历史日志,2>&1 把错误输出并到同一文件,这样脚本异常退出的原因也在内。写在命令末尾即可,例如 0 0 * * 0 /path/weekly-report.sh >> /var/log/report.log 2>&1。想做定时清理时也可直接交给 logrotate:30 3 * * * /usr/bin/logrotate /etc/logrotate.conf。若只是确认调度链路是否通,可先加一条最小任务:* * * * * echo test >> /tmp/cron-test 2>&1,一分钟后看文件是否多了一行。
我写的时间明明对,为什么总是差一小时?
因为 cron 按服务器的系统时区解释时间,而不是你的本地时区。先执行 date 看服务器当前时间和时区,如果服务器跑在 UTC 而你在 UTC+8,那么「每天 0 点」在服务器上是 UTC 0 点,也就是你本地早上 8 点。两个解决办法:要么把服务器时区配成你所在的时区,要么在 crontab 顶部写 CRON_TZ=Asia/Shanghai 单独为 cron 指定时区,这样既不动系统时区,也不会影响其它依赖系统时间的程序。特殊字符串 @daily / @midnight 等同样受该时区影响,含义是「该时区的午夜」。
想表达「每天一次」和「工作日一次」,字段该怎么填?
crontab 是五列:分 时 日 月 周,分别取 0-59、0-23、1-31、1-12、0-6(0 表示周日)。所以「每天凌晨 2 点」写 0 2 * * *,前三段分/时固定,* 表示日、月、周每天都执行。「工作日 9 点」写 0 9 * * 1-5,末段用 1-5 只覆盖周一到周五。每小时执行是 0 * * * *,也可用等价简写 @hourly。多个不连续取值用逗号并列,如 10 2 * * 6,7 表示周六周日 2:10。步长用斜杠:*/15 * * * * 是每 15 分钟,0 */2 * * * 是每 2 小时。注意周字段建议统一用 0,别混用 7。
由 巧匠 维护
公开更新于 2026年7月21日,内容持续校对官方文档。