[英]How to get the environment variables of a subprocess after it finishes running?
[英]How to get environment variables after running a subprocess
我正在使用subprocess.call
從我正在集成的另一個應用程序中執行 shell 腳本。 此腳本使用export MY_VAR=foo
設置環境變量。 接下來,我需要使用 shell 腳本設置的環境在子進程上執行更多命令。
如何從子進程中提取環境狀態? 它只返回errno
代碼。
即我想運行:
subprocess.call(["export", "MY_VAR=foo"]
subprocess.call(["echo", "$MY_VAR"]) # should print 'foo'.
我知道我可以使用env
關鍵字設置環境,但我的問題的重點是如何獲取子進程設置的環境變量。 在外殼可以source
任何腳本得到它的聲明環境變量。 python中的替代方案是什么?
這是不可能的,因為環境只在子進程中改變。 您可能會從那里將其作為輸出返回到 STDOUT、STDERR - 但是一旦子進程終止,您就無法從中訪問任何內容。
# this is process #1
subprocess.call(["export", "MY_VAR=foo"]
# this is process #2 - it can not see the environment of process #1
subprocess.call(["echo", "$MY_VAR"]) # should print 'foo'.
我最近遇到了這個問題。 由於 Python 上游的原因,這似乎是一個難題: posix_spawn
沒有提供讀取生成進程的環境變量的方法, 也沒有任何簡單的方法來讀取正在運行的進程的環境。
Bash 的source
特定於在 bash 解釋器中運行 bash 代碼:它只是在當前 bash 解釋器中評估文件,而不是啟動子進程。 如果您從 Python 運行 bash 代碼,則此機制無法工作。
可以創建一個專門用於從 Python 運行 bash 代碼的單獨機制。 以下是我能管理的最好的。 有一個不那么脆弱的解決方案會很好。
import json
import os
import subprocess
import sys
from contextlib import AbstractContextManager
class BashRunnerWithSharedEnvironment(AbstractContextManager):
"""Run multiple bash scripts with persisent environment.
Environment is stored to "env" member between runs. This can be updated
directly to adjust the environment, or read to get variables.
"""
def __init__(self, env=None):
if env is None:
env = dict(os.environ)
self.env: Dict[str, str] = env
self._fd_read, self._fd_write = os.pipe()
def run(self, cmd, **opts):
if self._fd_read is None:
raise RuntimeError("BashRunner is already closed")
write_env_pycode = ";".join(
[
"import os",
"import json",
f"os.write({self._fd_write}, json.dumps(dict(os.environ)).encode())",
]
)
write_env_shell_cmd = f"{sys.executable} -c '{write_env_pycode}'"
cmd += "\n" + write_env_shell_cmd
result = subprocess.run(
["bash", "-ce", cmd], pass_fds=[self._fd_write], env=self.env, **opts
)
self.env = json.loads(os.read(self._fd_read, 5000).decode())
return result
def __exit__(self, exc_type, exc_value, traceback):
if self._fd_read:
os.close(self._fd_read)
os.close(self._fd_write)
self._fd_read = None
self._fd_write = None
def __del__(self):
self.__exit__(None, None, None)
例子:
with BashRunnerWithSharedEnvironment() as bash_runner:
bash_runner.env.pop("A", None)
res = bash_runner.run("A=6; echo $A", stdout=subprocess.PIPE)
assert res.stdout == b'6\n'
assert bash_runner.env.get("A", None) is None
bash_runner.run("export A=2")
assert bash_runner.env["A"] == "2"
res = bash_runner.run("echo $A", stdout=subprocess.PIPE)
assert res.stdout == b'2\n'
res = bash_runner.run("A=6; echo $A", stdout=subprocess.PIPE)
assert res.stdout == b'6\n'
assert bash_runner.env.get("A", None) == "6"
bash_runner.env["A"] = "7"
res = bash_runner.run("echo $A", stdout=subprocess.PIPE)
assert res.stdout == b'7\n'
assert bash_runner.env["A"] == "7"
不確定我在這里看到了問題。 您只需要記住以下幾點:
所以讓setupVars.sh
像這樣:
export vHello="hello"
export vDate=$(date)
export vRandom=$RANDOM
並使printVars.sh
像這樣:
#!/bin/bash
echo $vHello, $vDate, $vRandom
並使用以下命令制作該可執行文件:
chmod +x printVars.sh
現在你的 Python 看起來像這樣:
import subprocess
subprocess.call(["bash","-c","source setupVars.sh; ./printVars.sh"])
輸出
hello, Mon Jul 12 00:32:29 BST 2021, 8615
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.