How to get the environment variables of a subprocess after it finishes running?
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
精彩评论