相对路径(包含斜杠的路径)永远不会在任何路径中被检查PATH
, 无论你做什么。它们是相对于当前工作目录仅有的。如果需要解析相对路径,则必须搜索PATH
手动。
如果你想运行一个程序相对于Python脚本的位置, use __file__
然后从那里找到程序的绝对路径,然后在中使用绝对路径Popen
.
在当前进程的环境变量中搜索PATH
有Python 错误跟踪器中的一个问题关于 Python 如何处理裸命令(无斜杠)。基本上,在 Unix/Mac 上Popen表现得像os.execvp
当争论env=None
(在最后观察到并记录了一些意想不到的行为):
在 POSIX 上,该类使用os.execvp()
-类似的行为来执行子程序。
这实际上对两者都是如此shell=False
and shell=True
, 假如env=None
。函数文档中解释了此行为的含义os.execvp:
末尾处包含“p”的变体(execlp()
, execlpe()
, execvp()
, and execvpe()
)将使用PATH
用于定位程序的环境变量file。当环境被替换时(使用其中之一exec*e
变体,将在下一段中讨论),新环境被用作PATH
多变的。
For execle()
, execlpe()
, execve()
, and execvpe()
(请注意,这些都以“e”结尾),env参数必须是用于定义新进程的环境变量的映射(这些变量用于代替当前进程的环境);功能execl()
, execlp()
, execv()
, and execvp()
都会导致新进程继承当前进程的环境。
引用的第二段意味着execvp
将使用当前进程的环境变量。结合第一段引用的段落,我们推断出execvp
将使用环境变量的值PATH
来自当前进程的环境。这意味着Popen
看看的价值PATH
就像 Python 刚推出时一样(运行的PythonPopen
实例化)并且没有任何改变os.environ
会帮助你解决这个问题。
另外,在 Windows 上shell=False
, Popen
不注意PATH
根本不会,并且只会相对于当前工作目录进行查找。
What shell=True
does
如果我们通过会发生什么shell=True
to Popen
?在这种情况下,Popen只需调用 shell:
The shell参数(默认为False
) 指定是否使用shell作为程序来执行。
也就是说,Popen
相当于:
Popen(['/bin/sh', '-c', args[0], args[1], ...])
换句话说,与shell=True
Python会直接执行/bin/sh
,无需任何搜索(传递参数executable
to Popen
可以改变这一点,似乎如果它是一个没有斜杠的字符串,那么它会被Python解释为shell程序的名称来在值中搜索PATH
从当前进程的环境中,即在该情况下搜索程序时shell=False
如上所述)。
反过来,/bin/sh
(或者我们的外壳executable
)将寻找我们想要在其自己的环境中运行的程序PATH
,这与PATH
Python(当前进程)的,从上面短语“也就是说...”之后的代码推导出来(因为该调用有shell=False
,所以这是前面已经讨论过的情况)。因此,execvp
-类似的行为是我们从两者中得到的shell=True
and shell=False
, 只要env=None
.
Passing env
to Popen
那么如果我们通过了会发生什么env=dict(PATH=...)
to Popen
(因此定义一个环境变量PATH
在将要运行的程序的环境中Popen
)?
在这种情况下,新环境用于搜索要执行的程序。引用文档Popen
:
If env is not None
,它必须是一个定义新进程的环境变量的映射;这些用于代替继承当前进程环境的默认行为。
结合上述观察,并通过实验使用Popen
, 这意味着Popen
在这种情况下,其行为类似于函数os.execvpe. If shell=False
, Python 在新定义的中搜索给定的程序PATH
。正如上面已经讨论过的shell=True
,在这种情况下,程序是/bin/sh
,或者,如果程序名称与参数一起给出executable
,然后在新定义的中搜索这个替代(shell)程序PATH
.
另外,如果shell=True
, then 壳内shell 将用来查找给出的程序的搜索路径args
的值是PATH
传递给Popen
via env
.
So with env != None
, Popen
搜索键的值PATH
of env
(如果有钥匙PATH
存在于env
).
传播除以下之外的环境变量PATH
作为参数
关于环境变量有一个警告,除了PATH
:如果命令中需要这些变量的值(例如,作为正在运行的程序的命令行参数),那么即使这些变量存在于env
给予Popen
,如果没有,它们将不会被解释shell=True
。
这很容易避免,无需更改shell=True
:将这些值直接插入list
争论args
被赋予Popen
。 (另外,如果这些值来自Python自己的环境,则该方法os.environ.get
可以用来获取它们的值)。
Using /usr/bin/env
如果您只是需要路径评估并且并不真正想通过 shell 运行命令行,并且使用的是 UNIX,我建议使用env
代替shell=True
, as in
path = '/dir1:/dir2'
subprocess.Popen(['/usr/bin/env', '-P', path, 'progtorun', other, args], ...)
这可以让你通过一个不同的PATH
to the env过程(使用选项-P
),它将使用它来查找程序。它还避免了 shell 元字符的问题以及通过 shell 传递参数的潜在安全问题。显然,在 Windows 上(几乎是唯一没有/usr/bin/env
)你需要做一些不同的事情。
About shell=True
引用Popen
文档:
If shell is True
,建议通过args
作为字符串而不是序列。
Note:阅读安全考虑使用前的部分shell=True
.
意外的观察结果
观察到以下行为:
-
这个呼吁引发了FileNotFoundError
,正如预期的那样:
subprocess.call(['sh'], shell=False, env=dict(PATH=''))
-
这个调用发现sh
,这是意想不到的:
subprocess.call(['sh'], shell=False, env=dict(FOO=''))
Typing echo $PATH
在打开的外壳内显示PATH
value 不为空,也不同于PATH
在Python环境中。所以看来PATH
确实不是从 Python 继承的(正如预期的那样,存在env != None
),但是,它仍然是PATH
是非空的。不知道为什么会出现这种情况。
-
这个呼吁引发了FileNotFoundError
,正如预期的那样:
subprocess.call(['tree'], shell=False, env=dict(FOO=''))
-
这发现tree
,正如预期的那样:
subprocess.call(['tree'], shell=False, env=None)