Skip to content
DevOps2026-09-234 分钟阅读

Linux crontab 定时任务实战:命令、日志与排查

crontab 是一个程序,不只是一条语法

很多文章把 crontab 讲成"五个字段的表达式",但真上服务器排障时你会发现:表达式只是入门,crontab 是 Linux 上的定时任务服务(cron daemon)的管理入口。它决定你的任务跑不跑、以谁的身份跑、失败了去哪看。

一条完整的 crontab 条目长这样:

30 3 * * * /opt/scripts/backup.sh >> /var/log/backup.log 2>&1

前五段是时间规则(分 时 日 月 周),后面是命令本体。写规则这件事交给在线 Cron 表达式生成器点选即可,用解析器反向验证人类可读描述——本篇聚焦表达式之外的那一半:安装、查看、排查。

三个必会命令,和一个危险按钮

crontab -e    # 编辑当前用户的定时任务
crontab -l    # 列出当前用户的定时任务
crontab -r    # 删除当前用户的全部任务(没有确认!)

-r 紧挨着 -e,手滑误触会静默清空全部任务,是运维圈的经典事故。两个防御动作:

  1. 改动前先备份:crontab -l > ~/crontab.bak.$(date +%F)
  2. 想删单条任务时,永远用 crontab -e 进去注释那一行(行首加 #),而不是 -r 重来。

crontab -e 保存后大多数发行版会自动重载,不需要重启服务;但直接编辑 /var/spool/cron/ 下的文件不会触发重载——别绕过 crontab 命令去改文件。

用户 crontab、/etc/crontab 与 /etc/cron.d 的区别

| 位置 | 格式 | 适用场景 | |---|---|---| | crontab -e(/var/spool/cron/) | 5 字段,以当前用户身份执行 | 个人/应用账户的任务 | | /etc/crontab | 6 字段,多一个"用户"字段 | 系统级任务的传统位置 | | /etc/cron.d/xxx | 同 /etc/crontab 的 6 字段 | 软件包和运维脚本的推荐位置,一个文件一组任务 |

/etc/cron.d/ 下的文件名只能用字母、数字、下划线和连字符——带点号的文件名(如 backup.sh)会被 cron 直接忽略,这是"文件都在就是不跑"的头号原因。文件权限必须是 0644 且属主 root,否则同样被拒。

@reboot 与 @daily:七个快捷别名

@reboot /opt/scripts/init-cache.sh
@daily  /opt/scripts/cleanup.sh

完整别名:@reboot(启动时)、@yearly/@annually@monthly@weekly@daily/@midnight@hourly。在 /etc/cron.d/ 里同样可用。@reboot 特别适合容器或云主机重启后重建缓存、重挂载、拉起本地服务——比 systemd unit 轻得多。

任务没跑:五步排查法

按顺序走,绝大多数问题在前三步现形:

  1. cron 服务活着吗systemctl status crond(CentOS)或 systemctl status cron(Debian/Ubuntu)。
  2. 看 cron 自己的日志grep CRON /var/log/cron(CentOS)或 /var/log/syslog(Ubuntu)。能查到执行记录说明调度正常,问题在脚本;完全没有记录说明调度层就断了。
  3. 手动执行那行命令:把条目里的命令原样复制到 shell 里跑一遍。cron 环境与你交互 shell 的环境不一样(下一步展开),这是"我手动能跑、cron 不跑"的根源。
  4. 检查 PATH:cron 执行时通常只有 /usr/bin:/bin。脚本里 python3nodedocker 若装在 /usr/local/bin,就会静默找不到。解法:脚本里全部用绝对路径,或在 crontab 顶部显式声明 PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
  5. 看输出重定向:没有重定向时,任务输出发给本地邮件(很多服务器没配 mail,静默丢弃)。排障期务必加 >> /path/log 2>&1,上线后再决定是否保留。

时区坑:cron 用的是系统时区

0 3 * * * 在一台 TZ=UTC 的云服务器上跑的是北京时间上午 11 点,不是凌晨 3 点。三个自查动作:

timedatectl                    # 看服务器当前时区
date                           # 确认当下时间
ls -l /etc/localtime           # 时区链接指向

团队跨时区协作时,写 crontab 前先用时区转换器对齐一次"北京 3:00 = 服务器几点",把换算结果写进任务注释。Docker 容器内默认 UTC,且容器里通常没有 cron 服务——容器内定时任务应考虑宿主机 cron 调 docker exec,或改用 systemd timer / 应用内的调度库。

秒级任务:cron 做不到,别硬凑

标准 cron 最小粒度是分钟。网上流传的"每 30 秒"写法(同一任务写两行,第二行 sleep 30)能用但脆弱——任务本身耗时超过 30 秒就会叠着跑。真正需要秒级时:

  • 循环 + sleep:脚本内 while true; do ...; sleep 30; done,由 systemd 或 supervisor 守护,最直观。
  • systemd timerOnUnitActiveSec=30s,原生支持秒级,还有日志(journalctl)和依赖管理。

顺带一提:调试定时任务时,把"下次到底什么时候触发"确认清楚很省心——用表达式解析器把规则翻译成自然语言读一遍,能拦住大量"以为它每天跑、其实它每月 3 号跑"的事故。

速查表

| 需求 | 表达式 | |---|---| | 每天凌晨 3:30 | 30 3 * * * | | 每周一早 9:00 | 0 9 * * 1 | | 每月 1 号 0:00 | 0 0 1 * * | | 每 5 分钟 | */5 * * * * | | 工作日每天 18:00 | 0 18 * * 1-5 | | 开机时 | @reboot |

写完规则先解析验证语义,再上服务器;改 crontab 前先 -l 备份;排障从 /var/log/cron 开始。这三句话能避开 crontab 九成的坑。