在 Linux 服务器上,crontab 常用于执行备份、日志清理、数据同步、监控脚本等定时任务。但很多任务在终端里手动运行正常,放进 crontab 后却没有执行,或者执行结果不符合预期。
这类问题通常不是 crontab 本身坏了,而是权限、时间表达式、路径、环境变量或特殊字符处理不正确。下面整理 5 个常见原因和对应解决方案,适合按顺序排查。
一、脚本没有可执行权限
如果脚本只有读写权限,没有执行权限,crontab 调用时可能会失败。
例如脚本权限如下:
-rw-r--r-- 1 user user 1234 Apr 3 10:00 backup.sh
这个权限表示脚本文件没有 x 执行权限。即使脚本内容正确,crontab 执行时也可能报错:
Permission denied
解决方案
给脚本增加执行权限:
chmod +x /home/user/scripts/backup.sh
然后确认权限已经变成可执行:
ls -l /home/user/scripts/backup.sh
正常情况下应能看到类似:
-rwxr-xr-x 1 user user 1234 Apr 3 10:00 backup.sh
如果不想给脚本增加执行权限,也可以通过解释器执行脚本,例如:
* * * * * /bin/bash /home/user/scripts/backup.sh
不过更推荐给脚本设置正确权限,并在脚本第一行写明解释器:
#!/bin/bash
二、定时任务的时间语法写错
crontab 的时间格式由 5 个字段组成:
分 时 日 月 星期 命令
很多任务没有按预期执行,是因为时间表达式理解错误。
错误示例 1:每天凌晨 3 点执行
如果写成:
* 3 * * * /path/to/script.sh
这不是“每天凌晨 3 点整执行”,而是“每天 3 点这一小时内每分钟执行一次”,也就是执行 60 次。
正确写法是:
0 3 * * * /path/to/script.sh
表示每天凌晨 3 点整执行一次。
错误示例 2:每 10 分钟执行一次
如果写成:
10 * * * * /path/to/script.sh
这不是每 10 分钟执行一次,而是每小时的第 10 分钟执行一次,例如 01:10、02:10、03:10。
正确写法是:
*/10 * * * * /path/to/script.sh
表示每 10 分钟执行一次。
错误示例 3:月份日期和星期几同时指定
如果写成:
0 0 1 * 1 /path/to/script.sh
很多人以为这是“每月 1 号且刚好是周一时执行”,但在常见 cron 行为中,日期和星期字段同时指定时,通常会在“每月 1 号”或“每周一”满足其中一个条件时执行。
如果只是想每月 1 号凌晨 3 点执行,应写成:
0 3 1 * * /path/to/script.sh
常见正确写法
每天凌晨 3 点整执行:
0 3 * * * /path/to/script.sh
每 10 分钟执行一次:
*/10 * * * * /path/to/script.sh
每月 1 号凌晨 3 点执行:
0 3 1 * * /path/to/script.sh
三、命令和脚本没有使用绝对路径
在终端里可以直接执行的命令,放到 crontab 里不一定能执行。原因是 crontab 的默认环境非常精简,PATH 变量通常不包含 /usr/local/bin、/home/user/bin 等目录。
例如下面这种写法容易出错:
* * * * * python3 backup.py >> backup.log 2>&1
可能出现的问题包括:
python3: command not found
backup.py: No such file or directory
backup.log: No such file or directory
因为 crontab 不知道 python3 在哪里,也不知道 backup.py 和 backup.log 应该在哪个目录下。
解决方案
使用命令、脚本和日志文件的绝对路径:
* * * * * /usr/bin/python3 /home/user/scripts/backup.py >> /var/log/backup.log 2>&1
可以先用下面命令查看程序路径:
which python3
which bash
which php
which node
如果脚本里还调用了其他命令,也建议使用绝对路径,或者在 crontab 顶部显式设置 PATH。
四、环境变量缺失
很多脚本在终端里执行正常,但在 crontab 中失败,是因为脚本依赖了环境变量。
例如 backup.sh 中使用了自定义变量:
#!/bin/bash
echo "备份到目录: $BACKUP_DIR"
tar -zcf $BACKUP_DIR/backup.tar.gz /data
如果 BACKUP_DIR 只定义在 ~/.bashrc 中,crontab 默认不会自动加载它。这样脚本执行时,$BACKUP_DIR 可能是空值,最终导致备份路径错误。
解决方案 1:在 crontab 顶部定义环境变量
编辑定时任务:
crontab -e
在文件顶部加入环境变量:
BACKUP_DIR=/data/backup
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
* * * * * /home/user/scripts/backup.sh >> /var/log/backup.log 2>&1
这种方式比较清晰,适合变量较少的场景。
解决方案 2:在脚本开头加载环境配置文件
也可以在脚本中加载用户环境配置:
#!/bin/bash
source /home/user/.bashrc
echo "备份到目录: $BACKUP_DIR"
tar -zcf $BACKUP_DIR/backup.tar.gz /data
不过要注意,.bashrc 中如果包含交互式终端相关逻辑,可能会影响脚本执行。生产环境中,更推荐为脚本单独准备一个简单的环境变量文件,例如:
source /home/user/scripts/.env
.env 文件内容示例:
BACKUP_DIR=/data/backup
这样更容易维护,也不容易受到终端配置影响。
五、特殊字符 % 没有转义
在 crontab 命令中,% 是特殊字符,会被解析为换行符。如果命令中直接使用 %,可能导致命令被截断。
例如:
* * * * * date +%Y-%m-%d >> /var/log/date.log
在 crontab 中,这条命令可能会被解析成异常格式,导致执行失败。
解决方案
在 crontab 中使用 % 时,需要加反斜杠转义:
* * * * * date +\%Y-\%m-\%d >> /var/log/date.log 2>&1
如果是在脚本文件里执行 date 命令,则不需要这样转义。例如脚本中可以正常写:
#!/bin/bash
date +%Y-%m-%d >> /var/log/date.log
然后在 crontab 中调用脚本:
* * * * * /bin/bash /home/user/scripts/date.sh
把复杂命令放进脚本文件中,通常比直接写在 crontab 里更容易维护。
六、建议增加日志,方便定位问题
排查 crontab 不执行时,最好给任务加上日志输出。否则任务失败后,很难判断到底是权限、路径还是环境变量问题。
例如:
* * * * * /bin/bash /home/user/scripts/backup.sh >> /var/log/backup.log 2>&1
其中:
>> /var/log/backup.log表示追加标准输出到日志文件。2>&1表示把错误输出也写入同一个日志文件。
如果没有权限写入 /var/log/,可以先写到用户目录:
* * * * * /bin/bash /home/user/scripts/backup.sh >> /home/user/backup.log 2>&1
也可以在脚本开头增加调试信息:
#!/bin/bash
echo "任务开始: $(date)"
echo "当前用户: $(whoami)"
echo "当前路径: $(pwd)"
echo "PATH: $PATH"
这些信息能快速判断 crontab 的运行环境是否符合预期。
推荐阅读:《Linux命令大全手册》
-
广告合作
-
QQ群号:4114653



