我在 Debian 12 上设置了以下 cron 作业:
/etc/cron.d/jonathan-test
:
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
* * * * * jonathan /home/jonathan/test1.sh >> /home/jonathan/test.log 2>&1
/home/jonathan/test1.sh
:
#!/usr/bin/env bash
export PATH="/home/jonathan/mytestdir:${PATH}"
echo "test1.sh -> PATH=${PATH}"
export PAAATH="this_is_a_test"
echo "test1.sh -> PAAATH=${PAAATH}"
exec "${HOME}/test2.sh"
/home/jonathan/test2.sh
:
#!/usr/bin/env bash
echo "test2.sh -> PATH=${PATH}"
echo "test2.sh -> PAAATH=${PAAATH}"
当它运行时,它会将以下内容写入/home/jonathan/test.log
:
test1.sh -> PATH=/home/jonathan/mytestdir:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
test1.sh -> PAAATH=this_is_a_test
test2.sh -> PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
test2.sh -> PAAATH=this_is_a_test
如您所见,$PATH
变量未被保留exec
。
这是我实际问题的一个简化示例,即从 cron 作业运行pyenvcron.d
。如果我将文件更改为以下内容:
SHELL=/bin/bash
PYENV_ROOT=/opt/pyenv
PATH=/opt/pyenv/shims:/opt/pyenv/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
* * * * * jonathan python --version >> /home/jonathan/test.log 2>&1
然后我将其写入输出文件:
/opt/pyenv/libexec/pyenv-exec: line 24: pyenv-version-name: command not found
它正确执行了/opt/pyenv/shims/python
。那只是一个运行的 bash 脚本pyenv exec python --version
。它正确执行了/opt/pyenv/bin/pyenv
,这是一个指向的符号链接/opt/pyenv/libexec/pyenv
,这是一个修改$PATH
为包含的bash 脚本/opt/pyenv/libexec
(是的,它确实导出了它!)并执行了/opt/pyenv/libexec/pyenv-exec
,这是另一个 bash 脚本,它试图PYENV_VERSION="$(pyenv-version-name)"
在第 24 行执行,这导致了上面的错误,因为/opt/pyenv/libexec
不在$PATH
。我把它缩小到上面的简化示例。当不从 cron 运行时,仅使用环境变量且没有 shell 集成的完全相同的pyenv
设置也可以正常工作。
仅供参考,这里没有sudo
任何地方,我也可以像其他用户一样重现它。所以它似乎与secure_path
无关/etc/sudoers
。
既然我已经明白了,我就来回答我自己的问题。
问题是我有一
$BASH_ENV
组自定义的。当以非交互方式调用时,bash 不会读取.profile
、.bash_profile
或.bashrc
(文档)。这是 cron 作业的常见问题,因为它们不会具有许多人期望的环境。但是,您可以使用$BASH_ENV
。当以非交互方式调用 bash 时,它的行为就像它这样做了:因此,为了获得一个一致的环境(无论我的脚本是否从 cron 运行),我指向了
$BASH_ENV
一个如下所示的文件:因此,我的 cron 作业将获得与从登录 shell 调用相同的环境。这有两个问题。首先,
/etc/profile
是定义$PATH
而不是添加。其次,我没有意识到$BASH_ENV
为每个子 shell 加载,所以我的$PATH
cron 作业不断为每个子 shell 重置。因此,我将其更改
/etc/profile
为检查特定路径$PATH
,如果不存在则添加它们。我将$BASH_ENV
文件更改为:(我可能不应该使用以 开头的变量名
BASH_
,但我想不出该怎么称呼它。)