如何在子进程运行结束后获取环境变量?

4
我正在寻找一种方法来实现这个,以便我可以将其传递给另一个子进程的环境。

我认为这取决于Shell。 - Blender
1
顺便感谢大家的帮助。 - user443850
5个回答

8
这是一个简单的函数,它在子进程中运行一个命令,然后将其环境提取到当前进程中。
它基于Fnord的版本,没有使用tempfile,并添加了一个标记行来区分SET命令和进程本身的任何输出。它不是绝对可靠的,但对于我的目的而言很有效。
def setenv(cmd):
    cmd = cmd + ' && echo ~~~~START_ENVIRONMENT_HERE~~~~ && set'

    env = (subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)
                     .stdout
                     .read()
                     .decode('utf-8')
                     .splitlines())

    record = False
    for e in env:
        if record:
            e = e.strip().split('=')
            os.environ[e[0]] = e[1]
        elif e.strip() == '~~~~START_ENVIRONMENT_HERE~~~~': 
            record = True

4
很不幸,一旦子进程退出,其环境就会消失。即使使用/proc文件系统在Unix上的特殊文件/proc/[pid]/environ,它也无法反映子进程所做的更改。
即使上述方法可行,你仍然需要解决竞争条件:父进程需要确定在子进程修改环境后立即读取环境的“正确时间”。为此,父进程需要与子进程协调,只要你正在协调,你可能就应该明确地进行通信。
你需要通过套接字、管道、共享内存等在父进程和子进程之间传递状态。多进程模块可以使这变得更加容易,让你通过队列或管道将数据从子进程传递到父进程。 更新:以下是使用multiprocessing模块让父进程与子进程共享值,并让子进程通过队列相互通信的快速草图。它使事情变得非常简单:
import os
from multiprocessing import Process, Manager, Queue

def worker1(d, q):
    # receive value from worker2
    msg = q.get()
    d['value'] += 1
    d['worker1'] = os.getpid(), msg

def worker2(d, q):
    # send value to worker1
    q.put('hi from worker2')
    d['value'] += 1
    d['worker2'] = os.getpid()

if __name__ == '__main__':
    mgr = Manager()
    d = mgr.dict()
    q = Queue()
    d['value'] = 1
    p1 = Process(target=worker1, args=(d,q))
    p1.start()
    p2 = Process(target=worker2, args=(d,q))
    p2.start()
    p1.join()
    p2.join()
    print d

结果:

{'worker1': (47395, 'hi from worker2'), 'worker2': 47396, 'value': 3}

嗨,我不需要在父进程和子进程之间交换环境,而是在子进程之间交换。 - user443850
获取进程环境的限制仍将适用。我已更新我的回答,简要介绍了使用multiprocessing模块让两个子进程相互通信,并与父进程共享数据的过程。希望能有所帮助。 - samplebias

2
在Windows中,你可以使用SET命令获取你想要的东西,就像这样:
import os, tempfile, subprocess

def set_env(bat_file):
    ''' Set current os.environ variables by sourcing an existing .bat file
        Note that because of a bug with stdout=subprocess.PIPE in my environment
        i use '>' to pipe out the output of 'set' into a text file, instead of
        of using stdout. So you could simplify this a bit...
    '''

    # Run the command and pipe to a tempfile
    temp = tempfile.mktemp()
    cmd = '%s && set > %s'%(bat_file,temp)
    login = subprocess.Popen(cmd, shell=True)
    state = login.wait()

    # Parse the output
    data = []
    if os.path.isfile(temp):
        with open(temp,'r') as file:
            data = file.readlines()
        os.remove(temp)

    # Every line will set an env variable
    for env in data:
        env = env.strip().split('=')
        os.environ[env[0]] = env[1]


# Make an environment variable
os.environ['SOME_AWESOME_VARIABLE']='nothing'

# Run a batch file which you expect, amongst other things, to change this env variable
set_env('C:/do_something_awesome.bat')

# Lets see what happened
os.environ['SOME_AWESOME_VARIABLE']
// RESULT: 'AWESOME'

现在,如果您可以使用此方法来读取 .bat 文件,然后根据需要使用它生成的环境变量,修改/添加它们,传递给新进程等等...


1

你能在第一个子进程中打印它们并在Python中处理该字符串吗?


有没有更好的方法,这样我就不必从 env 中解析脚本了? - user443850
好的,我最终做了这个。看起来这是最直接的方法... - user443850

0

Wade的回答几乎是完美的。显然,我的环境中有一个没有第二个元素的“'”,这破坏了env[0] = env[1]

def setenv(cmd):
    cmd = cmd + ' && echo ~~~~START_ENVIRONMENT_HERE~~~~ && set'

    env = (subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)
                     .stdout
                     .read()
                     .decode('utf-8')
                     .splitlines())

    record = False
    for e in env:
        if record:
            e = e.strip().split('=')
            if len(e) > 1:
                os.environ[e[0]] = e[1]
        elif e.strip() == '~~~~START_ENVIRONMENT_HERE~~~~': 
            record = True

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接