Python subprocess/Popen与修改后的环境

430 投票
10 回答
360215 浏览
提问于 2025-04-15 19:04

我觉得在一个稍微修改过的环境中运行外部命令是个很常见的情况。这就是我通常的做法:

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)

撰写回答