【问题标题】:Set Environmental Variables in Python with Popen使用 Popen 在 Python 中设置环境变量
【发布时间】:2023-11-23 06:20:02
【问题描述】:

我想通过 python 脚本在 linux 终端中设置一个环境变量。使用os.environ['BLASTDB'] = '/path/to/directory'时,我似乎可以设置环境变量。

但是,我最初尝试使用 subprocess.Popen 设置此变量,但没有成功。

import subprocess
import shlex

cmd1 = 'export BLASTDB=/path/to/directory'
args = shlex.split(cmd1)
p = subprocess.Popen(args, stdout=subprocess.PIPE).communicate()

为什么subprocess.Popen 设置环境变量 BLASTDB 为 '/path/to/directory' 失败?

注意: 使用时也会失败:

import os
os.system('export BLASTDB=/path/to/directory')

【问题讨论】:

  • 注意:Popen 不执行 shell。 export 是一个内置的 shell,因此如果您尝试运行该命令,您会收到“找不到文件错误”。使用shell=True 执行shell 命令(并注意其安全风险!!!)
  • 从我的测试看来,即使将shell=True 添加到命令中,它实际上并没有改变执行我的python脚本的环境变量,也没有改变python子shell的环境变量这是在执行时创建的。这可能只会改变这个特定子进程的环境,它(没有env 添加)将不允许我在修改了该子进程的环境的情况下向终端执行命令。
  • 我从来没有说过shell=True 会解决你的问题(否则我会把它作为答案发布)。使用shell=True 将是能够运行shell 命令的先决条件。如果没有它,您将收到错误,因为Popen 无法找到可执行文件。
  • 同意,我试图就我的具体问题向未来的访问者澄清。

标签: python environment-variables subprocess


【解决方案1】:

使用env参数为子进程设置环境变量:

proc = subprocess.Popen(args, stdout=subprocess.PIPE,
                        env={'BLASTDB': '/path/to/directory'})

the docs:

如果env不是None,它必须是定义环境的映射 新流程的变量;这些被使用而不是继承 当前进程的环境,这是默认行为。

注意:如果指定,env 必须提供程序所需的任何变量 执行。在 Windows 上,为了运行并行程序集 指定的 env 必须包含有效的 SystemRoot。


os.environ 可用于访问 python 进程的当前环境变量。如果您的系统也支持putenv,那么os.environ 也可以用于设置 环境变量(因此可以用来代替上面显示的Popen 的env 参数)。但是,对于某些操作系统,例如 FreeBSD 和 MacOS,设置 os.environ may cause memory leaks,因此设置 os.environ 并不是一个可靠的解决方案。


os.system('export BLASTDB=/path/to/directory') 运行一个子进程,该子进程仅为该子进程设置BLASTDB 环境变量。由于该子进程结束,它对后续的subprocess.Popen 调用没有影响。

【讨论】:

  • 我相信 OP 正在尝试为执行终端 /through/ Python 设置环境变量。您的答案将仅为子流程本身设置环境变量。
  • @Dologan:即使这是真的,设置环境变量的唯一目的是影响后续的子进程调用,在这种情况下使用env就是答案。
  • @unutbu:从我的测试来看,env 工作得很好,并为该特定子进程临时设置了环境变量BLASTDB,一切都顺利进行。谢谢!就我的目的而言,这就足够了。 os.environ 很好,因为它似乎为整个 python 子 shell 设置了环境变量,并且不需要在每个 subprocess.Popen 中指定 env
  • 其实根据os.environ的文档,它可以用来修改当前进程的环境(如果OS提供了putenv系统调用)。
  • @Bakuriu,在某些操作系统上setting environ causes memory leaks,所以它不是一个可靠的解决方案。但谢谢你的评论;我已经更新了我的答案以澄清这一点。
【解决方案2】:

据我所知,无论是 Python 还是 bash 本身,您都无法真正从子进程或子 shell 修改执行进程的环境。环境变量特定于您所在的特定进程(至少在您似乎正在使用的 Unix 上)。

任何产生的子进程通常都会继承那个环境,但只是它的一个副本。例如,如果您在终端会话中运行bash 并导出一个新的环境变量,一旦您退出该子shell,您的原始shell 将保持不变。运行 Python 也不例外。

【讨论】:

  • 到目前为止,我还无法修改执行进程的环境。对我来说,我只需要对特定的子进程命令进行临时调整。但是,我很好奇这是否可能。感谢您的信息。
  • 可以通过 env 关键字 arg 到 subprocess.Popen,如 unutbu 的答案所示
  • 原始问题表明 OP 希望为 linux 终端本身设置环境变量,而不仅仅是从 Python 本身执行的子进程。在这种情况下,据我所知,我的回答仍然有效且正确。