我需要更改Python应用程序调用的程序。不幸的是,我无法更改Python代码。我只能更改调用环境(尤其是PATH
)。但是不幸的是,Python的子进程模块似乎忽略了PATH
(至少在某些情况下)。
PATH
搜索要调用的二进制文件时,如何强制Python遵守?
为了说明问题,这里是MVCE。实际的Python应用程序正在使用subprocess.check_output(['nvidia-smi', '-L'])
,但是以下简化代码显示了相同的行为。
创建test.py
:
import osfrom subprocess import run run(['which', 'whoami'])run(['/usr/bin/env', 'whoami'])run(['whoami'])os.execvp('whoami', ['whoami'])
现在创建一个本地whoami
脚本并执行test.py
:
echo 'echo foobar' >whoami chmod +x whoami PATH=.:$PATH python3 test.py
在我的系统1上打印:
./whoami foobar konrad konrad
我希望此代码始终显示foobar
而不是konrad
。
我MVCE包括os.execvp
因为调用的subprocess
文件指出,
在POSIX上,该类使用
os.execvp()
类似行为来执行子程序。
不用说,从C调用的实际 execvp
POSIX API 确实尊重PATH
,因此这是Python特有的问题。
1 Ubuntu 18.04.2 LTS,Python 3.6.9。
根据我的评论,这是由于Python的实现execvp
与POSIX execvp
语义不一致。特别是Python 不会ENOEXEC
通过将文件解释为Shell脚本来对错误做出响应,并且需要显式的shebang。
创建文件为:
printf '#!/bin/sh\necho foobar\n' > ./whoami
使事情按预期工作
你的
echo 'echo foobar' >whoami chmod +x whoami
运行不正常。
即使设置了执行位,Python也不选择可执行文件,它不知道它需要先运行bash才能执行,因此它会跳过该路径并运行路径为/ usr的原始whoami。 / bin / whoami
添加shebang
echo "#!/bin/sh" > whoami echo 'echo foobar' >> whoami chmod +x whoami
在Unix风格的系统(包括Linux / OS X)上,shebang线(称为)告诉加载程序(或内核,或有时是shell)使用哪个程序来运行文件。最基本的说,您将指定python解释器的路径。
我怀疑如果您./whoami(设置了执行权限),则外壳程序正在执行一些额外的操作,因此您不必键入/ bin / sh $ PWD / whoami
如果你这样做
chmod -x whoami
你可以用特殊的
。./whoami(告诉外壳程序将其作为外壳程序脚本执行)。
注意execvp应该使用/ bin / sh而不是bash。也。./whoami将取决于您恰巧使用的Shell,大多数将“提供”文件而不是在另一个进程中执行(例如,对环境,工作目录等的更改将保留)
如果没有shebang或可执行文件头,则外壳程序仅将其自身用作默认解释器(但仅当通过./whoami;。./whoami进行调用时,无论文件是否可执行,它都会提供文件)。
execvp(POSIX,而不是Python)的混乱特性显然也可以做到这一点。在这种情况下,Python会失败,因为os.execvp实际上并未在幕后调用execvp,其相似之处仅在于名称。