我通常会遇到几个问题,因为cron执行脚本时通常没有我的环境设置。是否有一种方法可以像cron一样调用bash(?),以便在安装脚本之前测试脚本?
我通常会遇到几个问题,因为cron执行脚本时通常没有我的环境设置。是否有一种方法可以像cron一样调用bash(?),以便在安装脚本之前测试脚本?
* * * * * env > ~/cronenv
执行完后,请执行以下操作:
env - `cat ~/cronenv` /bin/sh
假设您的cron运行/bin/sh,这是默认设置,无论用户的默认shell是什么。
注:如果env
包含更高级的配置,例如PS1=$(__git_ps1 " (%s)")$
,它将会产生神秘的错误信息env: ": No such file or directory
。
/tmp/cronenv
,以避免在该上下文中混淆~
的含义。 - Max WilliamsCron默认只提供以下环境变量:
HOME
用户主目录LOGNAME
用户登录名PATH=/usr/bin:/usr/sbin
SHELL=/usr/bin/sh
如果您需要更多环境变量,您可以在crontab中调度表之前导入一个脚本,其中定义了您的环境变量。
两种方法:
导出 cron 环境并引用它:
添加
* * * * * env > ~/cronenv
将其添加到您的crontab中,让它运行一次,然后关闭它,再运行
env - `cat ~/cronenv` /bin/sh
现在你正在一个具有cron环境的sh
会话中。
将你的环境带到cron中
你可以跳过上面的练习,只需在cron作业前面执行. ~/.profile
即可。
* * * * * . ~/.profile; your_command
使用 screen
上述两种解决方案仍然失败,因为它们提供了一个连接到运行中的 X 会话的环境,并且具有访问 dbus
等的权限。例如,在 Ubuntu 上,nmcli
(网络管理器)将在上述两种方法中工作,但在 cron 中仍然会失败。
* * * * * /usr/bin/screen -dm
将上述行添加到cron中,让其运行一次,然后关闭。连接到您的屏幕会话(screen -r)。如果您正在检查屏幕会话是否已创建(使用ps
),请注意它们有时是大写的(例如ps | grep SCREEN
)
现在即使是nmcli
和类似的命令也会失败。
env -i $(cat ~/cronenv) /bin/sh
。 -i
与-
(--ignore-environment)相同,但更易懂。$(...)
与\
...``相同。 - wisbucky你可以运行:
env - your_command arguments
这将使用空环境来运行您的命令。
env - HOME="$HOME" LOGNAME="$USER" PATH="/usr/bin:/bin" SHELL="$(which sh)" command arguments
,这似乎可以解决问题。 - l0b0根据账户的shell不同
sudo su
env -i /bin/sh
或者sudo su
env -i /bin/bash --noprofile --norc
摘自http://matthew.mceachen.us/blog/howto-simulate-the-cron-environment-1018.html
六年后回答:环境不匹配问题是由 systemd
的“定时器”解决的问题之一,作为 cron 的替代品。无论您是通过 CLI 运行 systemd “服务”还是通过 cron,它都会收到完全相同的环境,避免了环境不匹配问题。
导致 cron 作业在手动传递时失败的最常见问题是由 cron 设置的默认限制性 $PATH
,在 Ubuntu 16.04 上是这样的:
"/usr/bin:/bin"
相比之下,Ubuntu 16.04 上 systemd
设置的默认$PATH
是:
"/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
因此,使用systemd计时器可以更轻松地找到二进制文件,从而有更好的机会。
但是,systemd计时器的缺点是设置它们需要更多的时间。首先,您需要创建一个“服务”文件来定义要运行的内容,然后创建一个“计时器”文件来定义运行的时间表,最后“启用”计时器以激活它。
创建一个 cron job,运行 env 命令并将标准输出重定向到文件中。 使用这个文件和 "env -" 命令来创建与 cron job 相同的环境。
#!/bin/bash
# Run as if it was called from cron, that is to say:
# * with a modified environment
# * with a specific shell, which may or may not be bash
# * without an attached input terminal
# * in a non-interactive shell
function usage(){
echo "$0 - Run a script or a command as it would be in a cron job, then display its output"
echo "Usage:"
echo " $0 [command | script]"
}
if [ "$1" == "-h" -o "$1" == "--help" ]; then
usage
exit 0
fi
if [ $(whoami) != "root" ]; then
echo "Only root is supported at the moment"
exit 1
fi
# This file should contain the cron environment.
cron_env="/root/cron-env"
if [ ! -f "$cron_env" ]; then
echo "Unable to find $cron_env"
echo "To generate it, run \"/usr/bin/env > /root/cron-env\" as a cron job"
exit 0
fi
# It will be a nightmare to expand "$@" inside a shell -c argument.
# Let's rather generate a string where we manually expand-and-quote the arguments
env_string="/usr/bin/env -i "
for envi in $(cat "$cron_env"); do
env_string="${env_string} $envi "
done
cmd_string=""
for arg in "$@"; do
cmd_string="${cmd_string} \"${arg}\" "
done
# Which shell should we use?
the_shell=$(grep -E "^SHELL=" /root/cron-env | sed 's/SHELL=//')
echo "Running with $the_shell the following command: $cmd_string"
# Let's route the output in a file
# and do not provide any input (so that the command is executed without an attached terminal)
so=$(mktemp "/tmp/fakecron.out.XXXX")
se=$(mktemp "/tmp/fakecron.err.XXXX")
"$the_shell" -c "$env_string $cmd_string" >"$so" 2>"$se" < /dev/null
echo -e "Done. Here is \033[1mstdout\033[0m:"
cat "$so"
echo -e "Done. Here is \033[1mstderr\033[0m:"
cat "$se"
rm "$so" "$se"
cron
使用系统定义的sh
来执行其作业。这可能是实际的 Bourne shell 或者 dash
、ash
、ksh
或者 bash
(或其他一些)通过符号链接关联到sh
而运行在 POSIX 模式。
最好的做法是确保你的脚本拥有所需,并且不要认为系统会为它们提供任何东西。因此,你应该使用完整的目录规范并自己设置环境变量,如$PATH
。0 0 * * 1 /path/to/executable >/dev/null 2>&1
,然后,在“executable”中,我将为 $PATH
等设置值,并使用完整的目录规范来输入和输出文件等。例如:/path/to/do_something /another/path/input_file /another/path/to/output_file
。 - Dennis Williamson