3 回答

TA貢獻1795條經驗 獲得超7個贊
我最近遇到了這個問題。由于Python上游的原因,這似乎是一個難題:posix_spawn
沒有提供讀取生成進程的環境變量的方法,也沒有任何簡單的方法來讀取正在運行的進程的環境。
Bashsource
專門用于在 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"

TA貢獻1829條經驗 獲得超4個贊
不確定我是否看到這里的問題。您只需要記住以下幾點:
每個啟動的子流程獨立于之前子流程中完成的任何設置
如果您想設置一些變量并使用它們,請在一個過程中完成這兩件事
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

TA貢獻1854條經驗 獲得超8個贊
這是不可能的,因為環境僅在子進程中改變。您可以從那里將其作為輸出返回到 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'.
添加回答
舉報