crontab默认使用/bin/sh执行脚本;CentOS 7中该shell指向bash的POSIX兼容模式,不支持[[ ]]、source、数组等bash特有语法,且PATH极窄、不加载用户配置文件,导致手动正常而定时失败。

crontab 默认用什么 Shell 执行脚本
在 CentOS 7 里,crond 守护进程执行 crontab 任务时,默认调用的是 /bin/sh,并不是 /bin/bash。也就是说,不管当前用户登录时用的是 bash 还是 zsh,到了定时任务这里,实际执行环境还是 /bin/sh。这一点并不是环境巧合,而是 crond 的源码里直接写死的,因此单靠用户环境变量,比如 $SHELL,并不能把它改过去。
为什么脚本在 crontab 里执行失败,但手动运行正常
常见现象:脚本含 [[ ]] 、source ~/.bashrc、数组、$(date -d ...) 等 bash 特有语法,手动执行没问题,放进 crontab 就报错或静默失败 —— 基本就是被 /bin/sh 解释器拒绝了。
/bin/sh在 CentOS 7 上通常指向bash的 POSIX 兼容模式,不支持[[、let、declare、source(只认.)、大括号扩展等- 环境变量缺失:
PATH极简(通常是/usr/bin:/bin),HOME可能不对,~展开失效 - 没有加载用户 profile 或 rc 文件,所以自定义 alias、函数、PATH 补充全都不生效
强制 cron 用 bash 执行单个脚本的 3 种可靠方式
不改系统级默认 shell(没必要也不推荐),而是让任务「自己指定解释器」:
- 在脚本第一行明确写
#!/bin/bash,且确保脚本有执行权限(chmod +x /path/to/script.sh),然后在 crontab 里直接调用该脚本路径 —— 这是最常用、最安全的做法 - 在 crontab 条目中显式调用 bash:
0 2 * * * /bin/bash /home/user/backup.sh >> /var/log/backup.log 2>&1 - 在脚本开头加
set -e -u和完整路径调用命令(如/usr/bin/date),避免依赖 PATH;用.替代source;用[ ]替代[[ ]]—— 适配/bin/sh,但牺牲可读性和功能
不要碰 /etc/crontab 的 SHELL= 行
别被 /etc/crontab 文件顶部那行 SHELL=/bin/bash 迷惑了,它只对 /etc/crontab 中声明的系统级任务有效,而且前提是这个文件由 root 用户维护和生效。至于普通用户通过 crontab -e 写进去的任务,对这一行是完全不认的——这属于 crond 的既定设计边界,并不是 bug。
强行修改它还可能被系统更新覆盖,或导致其他服务(如 logrotate)调用异常。真正要改,也只应在 /etc/crontab 里为特定 root 任务加 /bin/bash -c '...' 包裹,而不是全局改解释器。
关键点就一个:cron 不管你登录用啥 shell,它只认自己的规则;想用 bash,就在脚本头写死 #!/bin/bash 或 crontab 里显式调用 /bin/bash,别指望环境变量或全局配置替你兜底。