开发者

How to get the environment variables of a subprocess after it finishes running?

开发者 https://www.devze.com 2023-03-03 13:07 出处:网络
I\'m looking for a way to 开发者_运维技巧do this, so that I can pass it to the environment of another subprocess.Here\'s a simple function which runs a command in a subprocess, then extracts its envir

I'm looking for a way to 开发者_运维技巧do this, so that I can pass it to the environment of another subprocess.


Here's a simple function which runs a command in a subprocess, then extracts its environment into the current process.

It's based on Fnord's version, without the tempfile, and with a marker line to distinguish the SET command from any output of the process itself. It's not bulletproof, but it work for my purposes.

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


Unfortunately the child's environment will evaporate as soon as it exits, and even if you use the /proc filesystem on Unix special file /proc/[pid]/environ it won't reflect changes made by the child process.

Even if the above did work, you'd have a race condition: the parent would need to determine the "right time" to read the environment, ideally right after the child modified it. To do that the parent would need to coordinate with the child, and as long as you're coordinating you might as well be communicating explicitly.

You'd need to pass state between parent and child over a socket, pipe, shared memory, etc. The multiprocessing module can make this a bit easier, letting you pass data from child to parent via queues or pipes.

Updated Here's a quick sketch of using the multiprocessing module to let a parent process share values with child processes, and for child processes to communicate with one another across a queue. It makes it pretty simple:

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

Result:

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


In Windows you could use the SET command to get what you want, like this:

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'

So now if you can use this to read .bat files and then use the environment variables it generates as you please, modify/add to them, pass on to a new process... etc...


Can you print them out in the first subprocess and deal with that string in python?


Wade's answer was nearly perfect. Apparently I had a "'" in my environment with no second element - that was breaking 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
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号