为什么子进程忽略 PATH,我该如何更改它?
Why is subprocess ignoring PATH, and how can I change this?
我需要更改 Python 应用程序调用的程序。不幸的是 我无法更改 Python 代码。我只能更改调用环境(特别是 PATH
)。但不幸的是 Python 的 subprocess 模块似乎忽略了 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
调用,因为 the subprocess
documentation 指出
On POSIX, the class uses os.execvp()
-like behavior to execute the child program.
不用说,实际 execvp
POSIX API,从C调用,确实 尊重 PATH
,所以这是一个 Python 特定问题。
1 Ubuntu 18.04.2 LTS,Python 3.6.9.
根据我的评论,这是由于 Python 通过将文件解释为 shell 脚本来实现 execvp
not being consistent with POSIX execvp
semantics. in particular Python doesn't respond to ENOEXEC
errors,并且需要明确的 shebang。
创建文件为:
printf '#!/bin/sh\necho foobar\n' > ./whoami
使事情按预期工作
请注意,这已经有一段时间了:https://bugs.python.org/issue19948
你的
echo 'echo foobar' >whoami
chmod +x whoami
运行 不正确。
Python 没有将其拾取为可执行文件,即使设置了执行位,它也不知道它需要先 运行 bash 才能执行,因此在路径上跳过 运行s 路径为 /usr/bin/whoami
的原始 whoami
添加 shebang
echo "#!/bin/sh" > whoami
echo 'echo foobar' >> whoami
chmod +x whoami
在 Unix 风格的系统(包括 Linux/OS X)上,shebang 行——正如它的名字一样——告诉加载程序(或内核,或者偶尔 shell)使用哪个程序 运行 文件。在最基本的情况下,您需要指定 python 解释器的路径。
我怀疑如果你./whoami(设置了执行权限),shell 正在做一些额外的魔法所以你不必输入 /bin/sh $PWD/whoami
如果你这样做
chmod -x whoami
你可以使用特殊的
。 ./whoami(告诉 shell 将其作为 shell 脚本执行)。
注意 execvp 应该使用 /bin/sh 而不是 bash。还 。 ./whoami 将取决于 shell 你碰巧使用的是什么,大多数将“获取”文件而不是在另一个进程中执行它(即对环境、工作目录等的更改将保留)
如果没有 shebang 或可执行文件头,shell 简单地将自己用作默认解释器(但仅当通过 ./whoami 调用时;../whoami 不同,它获取文件,无论是否它是可执行的)。
execvp(POSIX,而不是 Python)的混乱性质显然也是如此。 Python 在这种情况下失败,因为 os.execvp 实际上并没有在后台调用 execvp,它的相似之处只是名义上的。
我需要更改 Python 应用程序调用的程序。不幸的是 我无法更改 Python 代码。我只能更改调用环境(特别是 PATH
)。但不幸的是 Python 的 subprocess 模块似乎忽略了 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
调用,因为 the subprocess
documentation 指出
On POSIX, the class uses
os.execvp()
-like behavior to execute the child program.
不用说,实际 execvp
POSIX API,从C调用,确实 尊重 PATH
,所以这是一个 Python 特定问题。
1 Ubuntu 18.04.2 LTS,Python 3.6.9.
根据我的评论,这是由于 Python 通过将文件解释为 shell 脚本来实现 execvp
not being consistent with POSIX execvp
semantics. in particular Python doesn't respond to ENOEXEC
errors,并且需要明确的 shebang。
创建文件为:
printf '#!/bin/sh\necho foobar\n' > ./whoami
使事情按预期工作
请注意,这已经有一段时间了:https://bugs.python.org/issue19948
你的
echo 'echo foobar' >whoami
chmod +x whoami
运行 不正确。
Python 没有将其拾取为可执行文件,即使设置了执行位,它也不知道它需要先 运行 bash 才能执行,因此在路径上跳过 运行s 路径为 /usr/bin/whoami
的原始 whoami添加 shebang
echo "#!/bin/sh" > whoami
echo 'echo foobar' >> whoami
chmod +x whoami
在 Unix 风格的系统(包括 Linux/OS X)上,shebang 行——正如它的名字一样——告诉加载程序(或内核,或者偶尔 shell)使用哪个程序 运行 文件。在最基本的情况下,您需要指定 python 解释器的路径。
我怀疑如果你./whoami(设置了执行权限),shell 正在做一些额外的魔法所以你不必输入 /bin/sh $PWD/whoami
如果你这样做
chmod -x whoami
你可以使用特殊的
。 ./whoami(告诉 shell 将其作为 shell 脚本执行)。
注意 execvp 应该使用 /bin/sh 而不是 bash。还 。 ./whoami 将取决于 shell 你碰巧使用的是什么,大多数将“获取”文件而不是在另一个进程中执行它(即对环境、工作目录等的更改将保留)
如果没有 shebang 或可执行文件头,shell 简单地将自己用作默认解释器(但仅当通过 ./whoami 调用时;../whoami 不同,它获取文件,无论是否它是可执行的)。
execvp(POSIX,而不是 Python)的混乱性质显然也是如此。 Python 在这种情况下失败,因为 os.execvp 实际上并没有在后台调用 execvp,它的相似之处只是名义上的。