我需要更改由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
。
我的最小可复现示例包括os.execvp
的调用,因为 subprocess 文档指出:
在 POSIX 上,该类使用
os.execvp()
的行为来执行子进程程序。
不用说,实际的execvp
POSIX API 从 C 调用时会尊重PATH
,所以这是一个特定于 Python 的问题。
1 Ubuntu 18.04.2 LTS,Python 3.6.9。
whoami
脚本开头放置#!/bin/sh
,那么我会得到你期望的行为,省略它是有意为之吗?没有加上这个会导致脚本运行时出现ENOEXEC (Exec format error)
。 - Sam Mason#!/bin/sh
可以解决它。这是有道理的 - 在strace
中,我看到execve("./whoami", ...) = -1 ENOEXEC (Exec format error)
,因为缺少 shebang,内核无法执行该文件 - 所以选择了/usr/bin/whoami
。 - KamilCukexecve
,而OP似乎故意使用execvp
。后者应该在直接执行时尝试使用/bin/sh
进行解释,如果出现ENOEXEC
错误。 - Sam Masonexecvp
也不是!)。但这确实解决了我的问题,如果你把它写成答案,我可以接受!我仍然很好奇为什么在这里需要它,而对于POSIX函数却不需要它(尽管现在我想想,我更惊讶的是POSIX函数不需要它)。 - Konrad Rudolph