VPS的Crontab任务设置了却没执行

写了个脚本,手动bash script.sh跑一遍好好的,扔进crontab设置成定时任务,到点却什么反应都没有,日志里也不报错,就是安安静静地什么都没发生,排查起来比报错还让人摸不着头脑。

手动跑和定时跑,压根不是同一个环境

这是最常见也最容易被忽略的根源——crontab执行任务时用的环境变量,跟你SSH登录之后手动跑命令用的环境变量,是两套完全不同的东西。你平时登录之后能直接敲nodepython3docker这些命令,靠的是~/.bashrc/etc/profile这些文件里定义好的PATH,把这些工具所在的目录加了进去。但cron执行任务的时候,根本不会去读取这些登录相关的配置文件,它用的是一套非常精简的默认环境,PATH通常只有/usr/bin:/bin这么两个目录,你手动装的那些工具,如果装在/usr/local/bin或者别的自定义路径下,在cron的世界里压根就"不存在",脚本一执行到那条命令,直接报"command not found",只不过这个报错默认没地方看,你压根不知道它失败了。

一个简单的验证办法

不确定是不是这个原因,加一条临时任务,把cron实际能看到的环境变量导出来看看:

1
* * * * * env > /tmp/cron_env.log

等一分钟之后打开这个文件,对比一下跟你正常登录之后执行env看到的结果,PATH这一项基本一眼就能看出差在哪。

怎么解决

最省事、也最推荐的做法是脚本里所有命令都用绝对路径,不依赖PATH去帮你找:

1
/usr/local/bin/node /home/user/script.js

而不是简简单单写node script.js寄希望于PATH自动找到它。找命令的绝对路径,用which查一下就行:

1
which node

如果不想每条命令都改成绝对路径,也可以在脚本开头显式声明PATH,或者干脆把需要的环境变量手动export一遍:

1
export PATH=$PATH:/usr/local/bin

别忘了这几个更基础的排查点

除了PATH这个最典型的坑,还有几个同样常见、优先级甚至更靠前的问题,一并确认一下:

  • crond这个服务本身有没有在跑systemctl status crond(或者cron,看发行版),服务都没启动,配再多定时任务也是白搭
  • 脚本有没有执行权限chmod +x script.sh,缺了这一步,手动bash执行能绕过去,cron直接调用可能会失败
  • 时间字段有没有写错:crontab的五个时间字段格式很容易手滑写错(比如把"每2小时"错写成"每小时的第2分钟"),跟命令本身没关系,纯粹是排班表写错了

顺带查日志

想知道任务到底执行了没有、失败在哪一步,给命令末尾加上日志重定向,比干等着猜靠谱得多:

1
* * * * * /path/to/script.sh >> /tmp/script.log 2>&1

顺带一提

这种"手动跑正常,交给系统自动执行就失灵"的情况,跟之前写的VPS改了Nginx配置,网站却还是老样子其实是同一类思路——不管是Nginx的配置生效,还是cron的执行环境,自动化的那一层跟你手动操作时的环境、状态都不完全一样,出问题的时候别光盯着命令本身对不对,环境差异往往才是真正的元凶。