crontab 定时任务不执行是 Linux 运维最高频的故障之一:脚本在终端手动能跑,写进 cron 却静默失败,日志里什么都没有。本文按”现象 → 定位 → 修复”的顺序,拆解 12 个最常见、也最容易踩的坑,并给出一套可直接照做的排查清单。
一、先确认:任务到底有没有跑
排查的第一步不是改配置,而是确认 cron 服务本身在不在、任务有没有被调度。很多”不执行”其实是服务没启动,或任务被写到了错误的用户表里。
# 查看 cron 服务状态(Debian/Ubuntu)
systemctl status cron
# CentOS/RHEL 用 crond
systemctl status crond
# 列出当前用户的任务
crontab -l
# 查看某用户(需 root)
crontab -l -u www-data
# 看 cron 自己的执行日志(路径因发行版而异)
grep CRON /var/log/syslog
journalctl -u cron --since "1 hour ago"
如果日志里压根没有对应任务的记录,说明任务没被加载或 cron 没运行;如果有记录但仍失败,往下看具体坑。
二、坑 1:PATH 环境变量缺失(最高频)
cron 执行环境极其”干净”,默认 PATH 通常只有 /usr/bin:/bin。你在终端能用的 python3、node、docker 在 cron 里很可能找不到,于是脚本第一行就报 command not found 然后退出。建议把 PATH 写成一行放在 crontab 文件最顶部,这样所有任务共享同一套查找路径,避免逐个脚本补绝对路径带来的疏漏。
# 在 crontab 顶部显式声明 PATH
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
*/5 * * * * /opt/scripts/backup.sh
# 或者脚本内部用绝对路径
*/5 * * * * /usr/bin/python3 /opt/scripts/backup.py
三、坑 2:相对路径与当前工作目录
cron 的工作目录默认是用户的 HOME(通常是 / 或 /root),不是脚本所在目录。脚本里写的 ./config.yaml、data/ 都会找不到。
# 在脚本开头 cd 到确定目录
*/5 * * * * cd /opt/scripts && /usr/bin/python3 backup.py
# 或在脚本里用绝对路径读取资源
CONFIG = "/opt/scripts/config.yaml"
四、坑 3:权限与用户身份
crontab -e 编辑的是当前用户的任务;root 的任务和用户 www-data 的任务互相隔离。脚本文件本身的权限、属主也要匹配,否则会报 Permission denied。
# 给脚本可执行权限
chmod +x /opt/scripts/backup.sh
# 确认属主
ls -l /opt/scripts/backup.sh
# 以正确用户身份安装任务
sudo crontab -e -u www-data
五、坑 4:Shell 差异(sh 而非 bash)
很多发行版的 cron 用 /bin/sh 执行任务,而 /bin/sh 往往指向 dash,不支持 bash 专属语法(如 source、数组、[[ ]])。脚本在 bash 里正常,进 cron 就报语法错。
# 在 crontab 顶部指定 shell
SHELL=/bin/bash
# 或脚本首行用 shebang 并显式调用
*/5 * * * * /bin/bash /opt/scripts/backup.sh
六、坑 5:输出与 MAILTO 把错误”吞掉”
cron 默认会把任务的标准输出/错误以邮件发给用户,但服务器通常没配邮件,于是错误信息凭空消失,你只看到”没执行”。这是排查时最坑的一点。
# 把输出重定向到日志,错误也一并收集
*/5 * * * * /opt/scripts/backup.sh >> /var/log/backup.log 2>&1
# 或在 crontab 顶部设定收件人
MAILTO="admin@example.com"
七、坑 6:脚本没有解释器或不可执行
忘记写 shebang(#!/bin/bash),或文件没有可执行位,cron 就会以默认 shell 去”读”它,结果一团乱。务必保证脚本首行 shebang 正确且带 +x。
八、坑 7:% 符号的特殊含义(date 命令重灾区)
crontab 里 % 是换行/命令分隔符,必须转义。很多人写 date +%Y%m%d 直接失败,就是因为 % 被 cron 当成特殊字符。
# 错误写法:% 未转义
* * * * * /bin/echo $(date +%Y%m%d) >> /tmp/t.log
# 正确写法:反斜杠转义
* * * * * /bin/echo $(date +\%Y\%m\%d) >> /tmp/t.log
九、坑 8:时区与系统时间不对
cron 按系统本地时区调度。如果服务器时区是 UTC 而你以为是北京时间,任务就会”差 8 小时执行”,看起来像没执行。容器环境尤其容易出现时区错位。
# 查看当前时区
timedatectl
# 确认 cron 重读时区(改完时区后重启 cron)
systemctl restart cron
十、坑 9:任务未加锁,互相覆盖或重复跑
任务执行时间超过调度间隔,或上一次还没结束就被再次触发,会造成资源争用、数据错乱。用 flock 加一把文件锁最省心。
# 同一时刻只允许一个实例运行
* * * * * /usr/bin/flock -n /tmp/backup.lock /opt/scripts/backup.sh
# 最多等待 10 秒拿锁,拿不到就跳过
* * * * * /usr/bin/flock -w 10 /tmp/backup.lock /opt/scripts/backup.sh
十一、坑 10:资源限制(磁盘满、文件描述符耗尽)
当系统磁盘写满,或进程打开的文件描述符耗尽时,任何需要写日志、建连接的任务都会失败。这类问题往往和 cron 本身无关,却表现为”任务不执行”。因此排查清单里专门把磁盘与 inode 检查单列一步,能快速排除这类干扰,避免在 cron 配置上白费力气。
# 检查磁盘剩余
df -h
# 检查 inode 是否耗尽
df -i
# 查看某进程打开的文件数
ls /proc/$(pgrep backup.sh)/fd | wc -l
十二、坑 11:SELinux / 容器 / systemd 环境隔离
开启了 SELinux 的机器可能拦截 cron 访问某些文件;容器里想用宿主机 cron 也常常因环境隔离而失效。更稳妥的做法是把定时任务交给容器编排或 systemd timer,而不是依赖容器内裸 cron。
十三、坑 12:cron 服务本身没启动或被禁用
最后也是最容易忽略的一点:cron 守护进程根本没运行,或新装系统后没开机自启。先确认服务状态(见第一节),必要时 systemctl enable --now cron。
十四、标准排查清单(照着做)
| 步骤 | 命令 / 动作 | 预期 |
|---|---|---|
| 1. 服务在跑? | systemctl status cron | active (running) |
| 2. 任务已加载? | crontab -l | 能看到你的任务 |
| 3. 有执行记录? | grep CRON /var/log/syslog | 出现对应任务行 |
| 4. 输出去哪了? | 任务加 >> log 2>&1 | 日志里有报错 |
| 5. 环境问题? | 任务首行打印 env | 对比终端 PATH |
| 6. 资源够吗? | df -h / df -i | 磁盘与 inode 有余量 |
十五、12 个坑速查表
| 坑 | 一句话 | 修复 |
|---|---|---|
| PATH 缺失 | 命令找不到 | crontab 顶部声明 PATH 或用绝对路径 |
| 相对路径 | 资源读不到 | 脚本里 cd 到绝对目录 |
| 权限/用户 | Permission denied | chmod +x、用对应用户安装 |
| Shell 差异 | bash 语法报错 | SHELL=/bin/bash 或显式调 bash |
| 输出被吞 | 无日志无报错 | 重定向到文件 + MAILTO |
| % 未转义 | date 等命令失败 | 用 \% 转义 |
| 时区不对 | 差几小时执行 | 统一时区并重启 cron |
| 未加锁 | 重复/覆盖跑 | flock 文件锁 |
| 资源耗尽 | 写不进 / 连不上 | 查磁盘、inode、文件描述符 |
| 环境隔离 | 容器/SELinux 拦 | 改 systemd timer 或编排 |
| 服务没起 | 根本没调度 | enable –now cron |
十六、小结
crontab 定时任务不执行,九成以上都逃不出”环境、权限、输出、路径”这四类。养成两个习惯即可少走弯路:第一,任务一律把输出重定向到日志;第二,脚本里用绝对路径、显式声明 PATH 与 SHELL。把上面的清单存成模板,下次再遇到 cron 沉默,照着走一遍就能定位。对于关键业务任务,建议叠加外部监控(如心跳文件加巡检脚本),即便 cron 静默失败也能被及时发现,而不是等到业务方投诉才后知后觉。更系统的开发环境排错思路也可以一并参考。




