Python从完成的子进程中获取环境

Posted

技术标签:

【中文标题】Python从完成的子进程中获取环境【英文标题】:Python get env from finished subprocess 【发布时间】:2022-01-22 09:17:42 【问题描述】:

python3.9

所以我有一个像这样的子进程


m_env = os.environ.copy()

process = subprocess.Popen(
    ['export', 'TEST=test'],
    env=m_env,
    stdout=subprocess.PIPE,
    stderr=subprocess.PIPE,
    shell=True
)
result = process.wait()

new_env = process.getenv() #how to get this so the new_env has the 'TEST=test' in it.

现在子进程本身正在修改环境,我想知道如何从进程中获取这个修改后的环境,示例是使用导出,但是我运行的脚本可能会更改或添加其他环境变量,所以我'我想获得整个环境。

【问题讨论】:

您需要在流程结束之前获取它,因为环境会随着流程而消失。 在其运行期间对其环境所做的任何更改都属于私有状态,因此尚不清楚为什么这比它在运行期间维护的任何其他变量更有意义。 另外,请注意['export', 'TEST=test'], shell=True 运行sh -c 'export' 'TEST=test',它根本不会将任何参数传递给export,因此即使您可以检索最终环境, TEST 不会参与其中。 好点,到目前为止,我认为一旦流程结束我就无法获得环境。这意味着我必须想出一些方法来输出脚本,然后一次性 env。 【参考方案1】:

@Pavel Hamerník 发布的解决方案是起点。在@Charles Duffy 评论讨论的指导下,以下解决方案最适合我的实施。

import os
import subprocess

def main():
    m_env = os.environ.copy()
    process = subprocess.Popen(
        ['/bin/bash', '-c', '. /path/script.sh && export > /tmp/env.txt'],
        env=m_env,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        shell=True
    )
    process.wait()
    print(process.stdout.read()) # read the stdout from script


if __name__ == '__main__':
    main()

注意:导出输出被定向到/tmp/ 中的文件。

【讨论】:

在这里使用shell=True 非常愚蠢——你正在运行sh -c '/bin/bash /path/to/script && export >/tmp/env.txt'。当您只需要一个时,为什么要启动 两个 shell,shbash 另外,/bin/bash /path/to/script.sh 在运行之前等待 bash 的副本退出 export;因此,export 看不到 script.sh 所做的环境更改,因为执行该脚本的 bash 副本在运行之前已导出。 您可能想要的是subprocess.Popen(['bash', '-c', '. /path/to/script.sh && export >/tmp/env.txt']),它来源脚本而不是将其作为子进程运行,所以export的副本在运行script.sh 的同一个解释器中运行。有一些警告——如果脚本调用exitexport 将不会运行——但这是业务固有的;一旦程序退出,它的环境变量无论如何都会丢失。 关于shell=True 的有效点,在我搞砸的过程中,我只是忘了删除它。至于环境可见性,你是对的,我在测试期间注意到了这一点,并修复了它。【参考方案2】:

子进程shell中可以打印所有的环境变量,但是之后要解析数据。

import os
import subprocess

def main():
    m_env = os.environ.copy()
    process = subprocess.Popen(
        ['export TEST=test && export'],
        env=m_env,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        shell=True
    )
    process.wait()
    print(process.stdout.read()) # all environment variables


if __name__ == '__main__':
    main()

编辑:将环境打印功能从printenv(解析时可能不明确)更改为export。感谢Charles Duffy

【讨论】:

我不建议尝试解析来自printenv 的输出。它的输出不允许您区分分配有export FOO=$'bar\nBAZ=qux' 的一个变量和两个变量export FOO=barexport BAZ=qux Easier-to-unambiguously-parse 可能是来自export TEST=test && cat /proc/self/environ 的输出(因为cat 是一个单独的进程而不是内置的shell,所以它在启动时的环境包括TEST ) 好的,那么 ['export TEST=test && export'] 怎么样。从二进制输出export TEST='test'\nexport WSLENV=''\nexport WSL_DISTRO_NAME='Ubuntu-20.04'\n解码时输出这样的数据 如果你有一个兼容 bash 的解析器,也许,但我仍然认为/proc/self/environ 中的 NUL 分隔形式更容易——只需将它拆分为 NUL 就可以了,因为根本没有任何东西被转义(并且没有任何东西需要被转义,因为环境变量不能包含 NUL)。 此外,export 的输出可能会根据哪个 shell 提供 /bin/sh 而发生变化,因此即使它在一台机器上以一种方式运行并不意味着它会以相同方式运行在另一个上。当然,procfs 是特定于 Linux 的,但在 Linux 中却非常一致。

以上是关于Python从完成的子进程中获取环境的主要内容,如果未能解决你的问题,请参考以下文章

从提升的子进程获取错误和标准输出

如何在python的子进程中执行一组语句?

从分离的子进程获取输出

Python - 在单独的子进程或线程中运行 Autobahn|Python asyncio websocket 服务器

如何从 Python 中的子进程向 SSH 传递命令

在bash脚本中获取命令的子进程