Python 如何使用自定义环境变量通过subprocess.Popen获取PID?

Python 如何使用自定义环境变量通过subprocess.Popen获取PID?,python,subprocess,popen,pid,Python,Subprocess,Popen,Pid,使用Python,如何使用修改过的环境变量运行子流程并获取其PID?我假设subprocess.Popen()沿着正确的轨道运行 在shell(bash)中,我会这样做: MY_ENV_VAR=value ./program_name arg1 arg2 etc & 它在后台运行program_name,传入“arg1”和“arg2”以及“etc”,并带有一个修改过的环境变量“MY_ENV_VAR”,其值为“value”。程序program_name要求将环境变量MY_ENV_VAR设

使用Python,如何使用修改过的环境变量运行子流程并获取其PID?我假设subprocess.Popen()沿着正确的轨道运行

在shell(bash)中,我会这样做:

MY_ENV_VAR=value ./program_name arg1 arg2 etc &
它在后台运行
program_name
,传入“arg1”和“arg2”以及“etc”,并带有一个修改过的环境变量“MY_ENV_VAR”,其值为“value”。程序
program_name
要求将环境变量
MY_ENV_VAR
设置为正确的值

如何在Python中完成相同的工作?我绝对需要这个过程的PID。(我的目的是让python脚本保持运行,并同时对
program\u name
正在做的一些事情执行检查,我需要进程ID以确保它仍在运行。)

我试过:

proc = subprocess.Popen(['MY_ENV_VAR=value', './program_name', 'arg1', 'arg2', 'etc'])
当然,它希望第一项是程序,而不是环境变量

还尝试:

environ = dict(os.environ)
environ['MY_ENV_VAR'] = 'value'
proc = subprocess.Popen(['./program_name', 'arg1', 'arg2', 'etc', env=environ])
我想很近,但没有雪茄。同样,这:

environ = dict(os.environ)
environ['MY_ENV_VAR'] = 'value'
proc = subprocess.Popen(['echo', '$MY_ENV_VAR'], env=environ)
我想这与“$MY_ENV_VAR”的字面意思相呼应,因为没有shell来解释它。好的,我尝试上面的方法,但用这句话:

proc = subprocess.Popen(['echo', '$MY_ENV_VAR'], env=environ, shell=True)
这很好也很好,只是回显的值是空的(显然不存在)。即使它确实有效,我也会得到shell的PID,而不是我试图启动的实际进程


我需要启动一个带有自定义环境变量的进程,并获取其PID(而不是shell的PID)。想法?

您的上一个版本非常接近,但不太接近

您不希望
$MY_ENV_VAR
成为
echo
的参数。
echo
程序将在其环境中具有
MY_ENV\u VAR
,但
echo
不进行任何环境变量扩展。在它到达
echo
之前,您需要通过shell对其进行扩展

这实际上可能与您的实际测试用例无关。您已经在所有测试中将环境变量传递给子进程,只是
echo
对该环境变量没有任何作用。如果您的实际程序只需要设置环境变量,则完成以下操作:

proc = subprocess.Popen(['./program_name', 'arg1', 'arg2', 'etc'], env=environ)
但是如果您的程序需要替换它,比如
echo
,那么您必须在参数传递到程序之前将其替换到参数中

最简单的方法是只给shell一个命令行,而不是参数列表:

proc = subprocess.Popen('echo "$MY_ENV_VAR"', env=environ, shell=True)
人们会告诉你,永远不要在
子流程中使用命令字符串,但原因是你总是想阻止shell以一种不安全的方式扩展变量等。在少数情况下,当你想让shell执行它的shell操作时,你需要一个命令字符串

当然,如果在大多数平台上使用shell,那么最终将得到shell的PID而不是实际程序的PID。除了做一些特定于平台的挖掘来枚举shell的子对象(或者用一些简单的
sh
代码来包装整个过程,从而间接给出子对象的PID),没有办法解决这个问题。外壳就是你正在运行的

另一种选择是在Python中扩展变量,而不是让shell来完成。那么你甚至不需要外壳:

……或者更简单地说:

proc = subprocess.Popen(['echo', os.environ['MY_ENV_VAR']])

这里有一个程序,可以描述当前的环境

#!/usr/bin/env python
##program_name
import os
for k,v in os.environ.iteritems():
    print k, '=', v
这里有一个程序调用另一个程序,但首先会更改环境

#!/usr/bin/env python
import subprocess, os
newenv = os.environ.copy()
newenv['MY_ENV_VAR'] = 'value'
args = ['./program_name', 'arg1', 'arg2', 'etc']
proc = subprocess.Popen(args, env=newenv)
pid = proc.pid
proc.wait()
print 'PID =', pid

你真的确定你的真实世界用例需要一个shell吗?当然,echo测试用例需要它,但在调用实际程序时通常不会/不应该使用它。@CharlesDuffy我只需要为该进程设置/传递环境变量的shell。如果我不需要这个外壳,我宁愿不用它。(但我确实需要将参数传递给程序并让它在后台运行。)环境变量正在传递给
proc=subprocess.Popen(['echo','MY_ENV_VAR',ENV=environ])示例中的子进程;该子流程根本不做任何事情来向您显示它们是,因为echo从不查看其环境。试试
subprocess.Popen(['env'],env=environ)
。@CharlesDuffy D'oh!就这样。你和abarnet差不多是在同一时间给我的。顺便说一句,如果你在使用
shell=True
时需要“real program”PID(同样,除非你真的知道你在做什么,否则你永远不应该使用),你可以让shell脚本用
exec
调用最后一个命令。对于本例:
exec echo“$MY_ENV_VAR”
;执行此操作将导致echo命令替换正在运行的shell进程,继承其PID。
请使用echo“$MY_ENV_VAR”
,否则,变量的内容是string split和glob-expanded.Cool,这(第一个)是有效的,我现在明白了原因——但我得到的是shell(sh)的PID,而不是echo进程。至于用Python扩展变量,这是一个好主意,但我可能不太清楚:被调用的程序希望设置该环境变量,我不能将其作为参数传入。@Matt:您可能正在阅读答案的旧版本。要获得环境变量集,而不是替换,只需使用
env
参数,而不使用shell。在您的
echo
测试中不起作用的唯一原因是echo需要替换变量,而不是设置。@abarnert您是对的。我同时得到了你的最新答案和查尔斯的评论。这似乎起到了作用。谢谢我没有意识到环境是直接传递给程序的,而不是一个shell(对我来说,是的)。你为什么要做
{k:v代表k,v在anything.iteritems()}
而不仅仅是
dict(anything)
?我没有仔细考虑。
#!/usr/bin/env python
import subprocess, os
newenv = os.environ.copy()
newenv['MY_ENV_VAR'] = 'value'
args = ['./program_name', 'arg1', 'arg2', 'etc']
proc = subprocess.Popen(args, env=newenv)
pid = proc.pid
proc.wait()
print 'PID =', pid