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,手滑误触会静默清空全部任务,是运维圈的经典事故。两个防御动作:
- 改动前先备份:
crontab -l > ~/crontab.bak.$(date +%F) - 想删单条任务时,永远用
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 轻得多。
任务没跑:五步排查法
按顺序走,绝大多数问题在前三步现形:
- cron 服务活着吗:
systemctl status crond(CentOS)或systemctl status cron(Debian/Ubuntu)。 - 看 cron 自己的日志:
grep CRON /var/log/cron(CentOS)或/var/log/syslog(Ubuntu)。能查到执行记录说明调度正常,问题在脚本;完全没有记录说明调度层就断了。 - 手动执行那行命令:把条目里的命令原样复制到 shell 里跑一遍。cron 环境与你交互 shell 的环境不一样(下一步展开),这是"我手动能跑、cron 不跑"的根源。
- 检查 PATH:cron 执行时通常只有
/usr/bin:/bin。脚本里python3、node、docker若装在/usr/local/bin,就会静默找不到。解法:脚本里全部用绝对路径,或在 crontab 顶部显式声明PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin。 - 看输出重定向:没有重定向时,任务输出发给本地邮件(很多服务器没配 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 timer:
OnUnitActiveSec=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 九成的坑。
相关工具
相关文章
docker run 转 docker-compose 完全指南:为什么转、怎么转、参数对照表
把 docker run 命令迁移到 docker-compose.yml 的完整指南:compose 相比裸 run 的五大优势、参数逐项对照表(端口/卷/环境变量/重启策略/网络)、常见坑(裸 -e 透传、匿名卷、--rm 语义),附在线转换器用法。
Permission denied (publickey) 怎么解决?Git 推送失败的 6 种原因与修复
git clone/push 报 Permission denied (publickey) fatal: Could not read from remote repository?覆盖公钥没生成、没加载进 agent、没添加到 GitHub/Gitee、多账号配错密钥、deploy key 权限、remote URL 写错六种根因,附 ssh -v 诊断方法。
npm ERR! ERESOLVE:peer dependency 冲突的四种解决策略
npm install 报 ERESOLVE unable to resolve dependency tree 怎么办?理解 peer dependency 的设计意图,掌握四种解决策略(版本修复 / legacy-peer-deps / overrides / dedupe)及其适用场景与风险。