【问题标题】:How to get PID via subprocess.Popen with custom environment variable?如何使用自定义环境变量通过 subprocess.Popen 获取 PID?
【发布时间】:2013-03-25 19:22:49
【问题描述】:

使用 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_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”相呼应,我想是因为没有外壳可以解释它。好的,所以我尝试上面的方法,但改用这一行:

proc = subprocess.Popen(['echo', '$MY_ENV_VAR'], env=environ, shell=True)

这很好,很漂亮,除了回显的值是空白的(显然不存在)。即使它确实有效,我也会得到 shell 的 PID,而不是我尝试启动的实际进程。

我需要使用自定义环境变量启动一个进程并获取它的 PID(不是 shell 的 PID)。想法?

【问题讨论】:

  • 真的确定您的实际用例需要一个外壳吗?当然,您需要它用于 echo 测试用例,但在调用实际程序时通常不会/不应该使用它。
  • @CharlesDuffy 我只需要 shell,只要设置/传递该进程的环境变量即可。如果我不需要外壳,我宁愿不使用它。 (但我确实需要将参数传递给程序并让它在后台运行。)
  • 环境变量被传递给proc = subprocess.Popen(['echo', '$MY_ENV_VAR'], env=environ) 示例中的子进程;该子进程根本不会做任何事情来向您表明它们是,因为 echo 从不查看它的环境。请改用subprocess.Popen(['env'], env=environ)
  • @CharlesDuffy D'oh!而已。你和 abarnet 几乎是同时给我的。
  • 顺便说一下,如果您在使用shell=True 时需要“真实程序的”PID(同样,除非您真的知道自己在做什么,否则永远不要使用它'正在做),您可以让 shell 脚本使用exec 调用最终命令。对于这个例子:exec echo "$MY_ENV_VAR";这样做会导致 echo 命令替换正在运行的 shell 的进程,继承它的 PID。

标签: python subprocess popen pid


【解决方案1】:

您的上一个版本非常接近,但还不够。

您不希望 $MY_ENV_VAR 成为 echo 的参数。 echo 程序将在其环境中包含 MY_ENV_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)

人们会告诉你,你永远不应该在subprocess 中使用命令字符串——但这样做的原因是你总是想防止 shell 以一种可能不安全/等的方式扩展变量等.在极少数情况下,当您想要 shell 执行其shell 的事情时,您想要一个命令字符串。

当然,如果您使用 shell,在大多数平台上,您最终会得到 shell 的 PID,而不是实际程序的 PID。如果没有做一些特定于平台的挖掘来枚举 shell 的孩子(或者将整个事情包装在一些简单的 sh 代码中,间接地给你孩子的 PID),没有办法解决这个问题。 shell 就是你正在运行的东西。

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

proc = subprocess.Popen(['echo', os.path.expandvars('$MY_ENV_VAR')])

……或者,更简单地说:

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

【讨论】:

  • 'echo "$MY_ENV_VAR"',请 - 否则,变量的内容是字符串拆分和全局扩展的。
  • 酷,(第一个)有效,我现在明白为什么了——但我得到的是 shell (sh) 的 PID,而不是 echo 进程。至于用 Python 扩展变量,这是一个好主意,但我可能不清楚:被调用的程序期望设置 environment variable,我不能将其传递为一个论点。
  • @Matt:您可能正在阅读旧版本的答案。要获取环境变量set,而不是submitted,只需使用env 参数即可,完全不用shell。在您的 echo 测试中不起作用的唯一原因是 echo 需要替换变量,而不是设置。
  • @abarnert 你是对的。我同时得到了你的最新答案和查尔斯的评论。这似乎可以解决问题。谢谢!我没有意识到环境是直接传递给程序而不是 shell(呃,就我而言)。
【解决方案2】:

这是一个吐出当前环境的程序。

#!/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

【讨论】:

  • 你为什么要做{k:v for k,v in anything.iteritems()} 而不是dict(anything)
  • 我没有考虑清楚。
猜你喜欢
  • 2020-01-06
  • 2019-04-06
  • 2014-01-24
  • 1970-01-01
  • 1970-01-01
  • 2022-11-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多