![为什么这个脚本从 cron 运行时会失败,但手动运行时却可以运行?](https://linux22.com/image/1400382/%E4%B8%BA%E4%BB%80%E4%B9%88%E8%BF%99%E4%B8%AA%E8%84%9A%E6%9C%AC%E4%BB%8E%20cron%20%E8%BF%90%E8%A1%8C%E6%97%B6%E4%BC%9A%E5%A4%B1%E8%B4%A5%EF%BC%8C%E4%BD%86%E6%89%8B%E5%8A%A8%E8%BF%90%E8%A1%8C%E6%97%B6%E5%8D%B4%E5%8F%AF%E4%BB%A5%E8%BF%90%E8%A1%8C%EF%BC%9F.png)
我有一个相对简单的 bash 脚本,直接调用时运行良好,但通过 cron 运行时失败。为什么会失败?我如何通过 cron 使其运行?
#!/bin/bash
apt-get update -y
apt-get upgrade -y
apt-get install boinc-client -y
一旦 cron 尝试运行它,手动调用将导致此错误:
dpkg was interrupted, you must manually run 'sudo dpkg --configure -a' to correct the problem.
但只要第一次手动执行,它就可以正常工作。
答案1
对此类问题的通常回答是,cron 作业在非交互式、非登录 shell 中运行,因此大多数 shell 启动文件(包括系统范围的文件/etc
和主目录中的个人点文件)都不是源文件(读入和执行的),因为大多数 shell 启动文件适用于登录 shell(登录计算机时看到的第一个 shell)或交互式 shell(连接到终端、ssh 会话或终端仿真器的 shell,因为用户通过所述终端与它们交互)。
因此,如果您将命令放入 cron 作业中,而该作业实际上依赖于某些环境设置(包括PATH
更改),而这些环境设置通常发生在/etc/profile
、/etc/bashrc
、~/.profile
或等位置~/.bashrc
,则该设置不会发生在 cron 作业中。 cron 文件格式允许您为作业指定环境变量,因此您可能需要指定BASH_ENV
或ENV
将其指向要源的 shell 启动脚本。请参阅手册页的“调用”部分bash(1)
。
答案2
这不算是一个答案,但我无法评论。建议:
将以下内容添加到您的 bash 脚本中。记录到邮件输出的最后一行将是失败的命令。
set -x set -e
- 确保您有 sendmail(安装提供包,例如
postfix
或esmtp
) - 安装邮件阅读器(推荐
mutt
) - 确保邮件送达
- 通过 postfix(可能由安装程序自动完成):添加
root: my-user-name
到/etc/aliases
或/etc/postfix/aliases
- 通过 cron:添加
MAILTO="my-user-name"
到适当的crontab
文件
- 通过 postfix(可能由安装程序自动完成):添加
- 确保您有 sendmail(安装提供包,例如
验证脚本是否能在不同环境中运行。指定 apt-get 的完整路径(可能不是罪魁祸首,因为已知已找到 apt-get)并在 X 之外的控制台(而非终端)中运行它。(某些 dpkg configure 脚本需要 X 会话)。
- 修改脚本以使用绝对路径,即
/usr/bin/apt-get update -y
(替换为正确的路径) - 通过按切换到控制台
ctrl-alt-f1
- 切换到root用户:
sudo -i
- 启动没有环境的非登录 shell:(
env -i /bin/bash --noprofile --norc
替换为正确的路径) - 运行脚本:
/my/full/path/to/cronscript
。它起作用了吗?
- 修改脚本以使用绝对路径,即
脚本是否有权限?您是否使用系统 crontab?(再次说明,可能不是罪魁祸首)
- 您已声明正在使用系统 crontab,因此跳过此步骤。
apt-get 是否需要会话支持(consolekit 或 systemd)。不过,这只是一个猜测。
- 了解的还不够多,无法提供帮助。
答案3
在几乎不了解发生了什么的情况下设法解决了这个问题。结果发现,即使我从 root crontab 运行,apt-get 命令仍然需要在前面添加 sudo。从逻辑上讲,我原本以为不需要这个,因为脚本已经“以 root 身份”执行,但是一旦我添加了 sudo... 一切都按预期工作。