Python subprocess/Popen与修改后的环境
我觉得在一个稍微修改过的环境中运行外部命令是个很常见的情况。这就是我通常的做法:
import subprocess, os
my_env = os.environ
my_env["PATH"] = "/usr/sbin:/sbin:" + my_env["PATH"]
subprocess.Popen(my_command, env=my_env)
我感觉可能还有更好的方法,这样做看起来还行吗?
10 个回答
48
在Python 3.5中,你可以这样做:
import os
import subprocess
my_env = {**os.environ, 'PATH': '/usr/sbin:/sbin:' + os.environ['PATH']}
subprocess.Popen(my_command, env=my_env)
这样我们就得到了一个os.environ
的副本,并且覆盖了PATH
的值。
这得益于PEP 448(额外的解包通用化)。
再举个例子。如果你有一个默认的环境(也就是os.environ
),还有一个字典想用来覆盖默认值,你可以这样表示:
my_env = {**os.environ, **dict_with_env_variables}
115
这要看具体是什么问题。如果你想复制并修改环境变量,一个解决方案可能是:
subprocess.Popen(my_command, env=dict(os.environ, PATH="path"))
不过,这个方法有点依赖于你替换的变量名必须是有效的Python标识符,通常它们都是这样的(你有多少次遇到环境变量名不是字母、数字或下划线的?或者是以数字开头的变量名?)。
如果不是这样的话,你可以写成这样:
subprocess.Popen(my_command, env=dict(os.environ,
**{"Not valid python name":"value"}))
在非常少见的情况下(你有多少次在环境变量名中使用控制代码或非ASCII字符?),如果环境变量的键是bytes
类型的话,你在Python 3中甚至不能使用这种写法。
正如你所看到的,这里使用的技巧(尤其是第一个)是因为环境变量的键通常是有效的Python标识符,并且在编码时是已知的,而第二种方法则存在一些问题。如果情况不是这样,你可能需要寻找其他方法。
611
我觉得如果你不打算修改当前进程的环境变量,使用 os.environ.copy()
会更好:
import subprocess, os
my_env = os.environ.copy()
my_env["PATH"] = f"/usr/sbin:/sbin:{my_env['PATH']}"
subprocess.Popen(my_command, env=my_env)