我对subprocess
在使用Popen()
时搜索可执行文件的方式感到困惑。如果给定子进程的绝对路径,它可以工作,但我正在尝试使用相对路径。我发现如果我设置环境变量PYTHONPATH然后我可以从该路径获取导入的模块,并且sys.path
中有PYTHONPATH,但它似乎没有帮助{{1}的行为}。我也尝试编辑subprocess.Popen
文件,将PYTHONPATH添加到sitecustomize.py
,就像这样
os.environ
并验证在启动python时,无论是交互式地,使用ipython,还是通过从命令行运行脚本,PYTHONPATH都成功出现在# copy PYTHONPATH environment variable into PATH to allow our stuff to use
# relative paths for subprocess spawning
import os
if os.getenv('PYTHONPATH') is not None and os.getenv('PATH') is not none:
os.environ['PATH'] = ':'.join([os.getenv('PATH'), os.getenv('PYTHONPATH')])
中。但是,os.environ
仍然不会在那里搜索可执行文件。如果没有指定subrocess.Popen
kwarg,我认为应该继承父母环境?接下来,我尝试明确地提供env
,首先制作env
的副本,其次只是提供os.getenv
,但仍然找不到可执行文件。现在我很难过。
希望一个例子可以帮助我更清楚地解释我的问题:
/目录/ subdir1 / some_executable
/dir/subdir2/some_script.py
env={'PATH': '/explicit/path/to/search/from'}
如果我在# some_script.py
from subprocess import Popen, PIPE
spam, eggs = Popen(['../subdir1/some_executable'], stdout=PIPE, stderr=PIPE).communicate()
并且我运行/dir/subdir2
它可以正常运行,但如果我在python some_script.py
并且我运行/dir
即使python subdir2/some_script.py
在/dir/subdir2
中,然后子进程将抛出os.environ['PATH']
。
答案 0 :(得分:54)
(从评论中填写详细信息以单独回答)
首先,无论你做什么,都不会在任何路径中检查相对路径(包含斜杠的路径)。它们仅与当前工作目录相关。如果需要解析相对路径,则必须手动搜索PATH,或者使用PATH来包含子目录,然后使用命令名称,如下面的建议所示。
如果要运行相对于Python脚本位置的程序 ,请使用__file__
并从那里找到程序的绝对路径,然后使用绝对路径Popen
中的路径。
其次,关于Python如何处理裸命令(没有斜杠)有an issue in the Python bug tracker。基本上,Unix / Mac Popen
在使用os.execvp
调用时使用shell=False
,这意味着它会查看Python启动时的PATH
的值并且没有任何变化os.environ
将帮助您解决这个问题。此外,在具有shell=False
的Windows上,它根本不关注PATH,而只会查看当前工作目录。
如果您只需要路径评估而不想真正通过shell运行命令行,并且在UNIX上,我建议使用env
而不是shell=True
,如{{1} }}。这允许您将不同的PATH传递给Popen(['/usr/bin/env', 'progtorun', other, args], ...)
进程,该进程将使用它来查找程序。它还避免了shell元字符的问题以及通过shell传递参数的潜在安全问题。显然,在Windows上(几乎是唯一没有env
的平台),你需要做一些不同的事情。
答案 1 :(得分:12)
您似乎对PATH
和PYTHONPATH
的性质感到有些困惑。
PATH
是一个环境变量,它告诉OS shell在哪里搜索可执行文件。
PYTHONPATH
是一个环境变量,它告诉Python解释器在哪里搜索要导入的模块。它与subprocess
查找可执行文件无关。
由于底层实现的不同,subprocess.Popen
将仅在非Windows系统上默认搜索路径(Windows总是搜索一些系统目录,但这与PATH
处理不同) 。扫描路径的唯一可靠的跨平台方法是将shell=True
传递给子进程调用,但这有其自身的问题(详见Popen
documentation)
但是,您的主要问题似乎是您将路径片段传递给Popen
而不是简单的文件名。只要你有一个目录分隔符,你就会禁用PATH
搜索,即使在非Windows平台上也是如此(例如,参见exec family of functions的Linux文档)。
答案 2 :(得分:2)
subprocess.Popen中的相对路径相对于当前工作目录而不是系统PATH的元素起作用。如果您从python subdir2/some_script.py
运行/dir
,则预期的可执行位置将为/dir/../subdir2/some_executable
,a.k.a /subdir2/some_executable
。
如果您肯定希望使用从脚本自己的目录到特定可执行文件的相对路径,那么最好的选择是首先从__file__
全局变量的目录部分构造一个绝对路径。
#/usr/bin/env python
from subprocess import Popen, PIPE
from os.path import abspath, dirname, join
path = abspath(join(dirname(__file__), '../subdir1/some_executable'))
spam, eggs = Popen(path, stdout=PIPE, stderr=PIPE).communicate()
答案 3 :(得分:0)
pythonpath设置为执行python解释器的路径。因此,在您的示例的第二种情况下,路径设置为/ dir而不是/ dir / subdir2 这就是你收到错误的原因。