为什么子流程忽略PATH,以及如何更改这一点



我需要更改Python应用程序调用的程序。不幸的是我无法更改Python代码。我只能更改调用环境(特别是PATH(。但不幸的是,Python的子流程模块似乎忽略了PATH(至少在某些情况下(。

在搜索要调用的二进制文件时,如何强制Python尊重PATH

为了说明这个问题,这里有一个MVCE。实际的Python应用程序使用subprocess.check_output(['nvidia-smi', '-L']),但下面的简化代码显示了相同的行为。

创建test.py:

import os
from 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调用的实际execvpPOSIX API确实尊重PATH,因此这是一个特定于Python的问题。


1Ubuntu 18.04.2 LTS,Python 3.6.9。

根据我的评论,这是由于Python对execvp的实现与POSIXexecvp语义不一致。特别是Python不会通过将文件解释为shell脚本来响应ENOEXEC错误,并且需要显式的shebang。

将文件创建为:

printf '#!/bin/shnecho foobarn' > ./whoami

使事情按预期进行

请注意,这已经知道一段时间了:https://bugs.python.org/issue19948

您的

echo 'echo foobar' >whoami
chmod +x whoami

运行不正确。

Python并没有获取可执行文件,即使设置了执行位,它也不知道需要先运行bash才能执行,因此它会跳过路径并运行原始的whoami,路径为/usr/bin/woami

添加舍邦

echo "#!/bin/sh" > whoami
echo 'echo foobar' >> whoami
chmod +x whoami

在Unix风格的系统(包括Linux/OSX(上,它所调用的shebang行即告诉加载程序(或内核,有时甚至是shell(使用哪个程序来运行文件。最基本的是,您需要指定一个到python解释器的路径。

我怀疑你/whoami(带有执行权限集(,shell正在做一些额外的魔术,所以你不必键入/bin/sh$PWD/whoami

如果你做

chmod-x whoami

你可以使用特殊的

/whoami(告诉shell将其作为shell脚本执行(。

请注意,execvp应该使用/bin/sh而不是bash。还有/whoami将取决于你碰巧使用的外壳,大多数都会";源";文件,而不是在另一个进程中执行它(即环境、工作目录等的更改将保留(

如果没有shebang或可执行文件头,shell只会将自己用作默认解释器(但只有当通过./whoami调用时;../whoami不同,它会获取文件源,而不管它是否可执行(。

execvp(POSIX,而不是Python(令人困惑的特性显然也起到了这个作用。Python在这种情况下失败了,因为os.execvp实际上并没有在后台调用execvp,它的相似之处只是名称上。

相关内容

  • 没有找到相关文章

最新更新