具有修改后环境的Python子进程/ Popen


284

我认为在环境稍有修改的情况下运行外部命令是很常见的情况。这就是我倾向于这样做的方式:

import subprocess, os
my_env = os.environ
my_env["PATH"] = "/usr/sbin:/sbin:" + my_env["PATH"]
subprocess.Popen(my_command, env=my_env)

我感觉到有更好的方法了。看起来还好吗?


10
os.pathsep对于跨平台工作的路径,也更喜欢使用而不是“:”。见stackoverflow.com/questions/1499019/...
阿米特

7
@phaedrus我不确定当他使用类似/usr/sbin:-) 的路径时是否非常相关
Dmitry Ginzburg

Answers:


402

我认为os.environ.copy()如果您不打算为当前进程修改os.environ会更好:

import subprocess, os
my_env = os.environ.copy()
my_env["PATH"] = "/usr/sbin:/sbin:" + my_env["PATH"]
subprocess.Popen(my_command, env=my_env)

>>> env = os.environ.copy >>> env ['foo'] ='bar'追溯(最近一次调用):文件“ <stdin>”,在<module>中的第1行,TypeError:'instancemethod'对象不支持项目分配
user1338062 2012年

5
@ user1338062要分配的实际方法os.environ.copyenv变量,但您需要分配调用该方法的结果os.environ.copy()env
chown

4
仅当shell=Truesubprocess.Popen调用中使用时,环境变量解析才真正起作用。请注意,这样做可能会带来安全隐患。
danielpops

内部subprocess.Popen(my_command,env = my_env)-什么是“ my_command”
avinash19年

@avinash- my_command只是要运行的命令。例如,它可以是/path/to/your/own/program任何其他“可执行”语句。
kajakIYD

64

这取决于问题所在。如果要克隆和修改环境,一种解决方案可能是:

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您甚至(在python3上)都无法使用该结构的环境键。

正如您所看到的,此处使用的技术(尤其是第一种)对环境键的好处通常是有效的python标识符,并且也预先知道(在编码时),第二种方法存在问题。如果不是这种情况,您可能应该寻找另一种方法


3
投票。我不知道你会写dict(mapping, **kwargs)。我以为是。注意:它os.environ会按照当前接受的答案中的@Daniel Burke的建议进行复制而不进行修改,但是您的答案更为简洁。在Python 3.5及更高版本中,您甚至可以这样做dict(**{'x': 1}, y=2, **{'z': 3})。见pep 448
jfs 2015年

1
这个答案解释了一些更好的方法(以及为什么这种方法不太好)将两个词典合并为一个新词典: stackoverflow.com/a/26853961/27729
krupan

@krupan:对于这个特定用例,您看到什么不利条件?(合并任意字典和复制/更新环境是不同的任务)。
jfs

1
@krupan首先,通常情况是环境变量将是有效的python标识符,这意味着第一个构造。在这种情况下,您的反对意见均不成立。对于第二种情况,您的主要反对意见仍然失败:关于非字符串键的观点在这种情况下不适用,因为无论如何在环境中基本上要求键都是字符串。
凌晨

@JFSebastian您是正确的,对于这种特定情况,此技术很好,我应该对自己做更好的解释。我很抱歉。我只是想帮助那些可能会想采用该技术的人(例如我自己),并将其应用于合并两个任意字典的一般情况(有一些陷阱,正如我所指出的答案)。
krupan

24

您可以使用它,my_env.get("PATH", '')而不是my_env["PATH"]PATH某种方式在原始环境中未定义,但除此之外,它看起来还不错。


20

使用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}

@avinash,检查出subprocess.Popen的文档。它是“程序参数序列或单个字符串”。
skovorodkin '19

10

要临时设置环境变量而不必复制os.envrion对象等,我可以这样做:

process = subprocess.Popen(['env', 'RSYNC_PASSWORD=foobar', 'rsync', \
'rsync://username@foobar.com::'], stdout=subprocess.PIPE)

4

env参数接受字典。您可以简单地使用os.environ,在其中添加键(如果需要,可以将其添加到dict的副本中)(用作键)Popen


如果您只想添加新的环境变量,这是最简单的答案。 os.environ['SOMEVAR'] = 'SOMEVAL'
安迪·弗雷

1

我知道已经回答了一段时间,但是有些人可能想知道一些有关在环境变量中使用PYTHONPATH而不是PATH的知识。我已经概述了使用cronjobs运行python脚本的说明,该说明以另一种方式(在此处找到)处理修改后的环境。认为这对像我这样需要的人仅比此答案所提供的要多。


0

在某些情况下,您可能只希望传递子流程所需的环境变量,但是我认为您通常具有正确的想法(这也是我的做法)。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.