2026-04-13 13:49:55 +08:00
|
|
|
|
"""子进程运行并把 stdout/stderr 合并;可选将逐行输出写入 loguru(测量/行为推理可关闭以减少日志)。"""
|
2026-04-09 11:54:30 +08:00
|
|
|
|
|
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
|
|
import os
|
|
|
|
|
|
import subprocess
|
|
|
|
|
|
from typing import Dict, List, Optional
|
|
|
|
|
|
|
|
|
|
|
|
from loguru import logger
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def run_subprocess_with_log(
|
|
|
|
|
|
cmd: List[str],
|
|
|
|
|
|
*,
|
|
|
|
|
|
cwd: str,
|
|
|
|
|
|
env: Optional[Dict[str, str]] = None,
|
|
|
|
|
|
log_name: str,
|
2026-04-13 13:49:55 +08:00
|
|
|
|
stream_to_logger: bool = True,
|
2026-04-09 11:54:30 +08:00
|
|
|
|
) -> subprocess.CompletedProcess[str]:
|
2026-04-13 13:49:55 +08:00
|
|
|
|
"""运行子进程,合并 stderr 到 stdout,可选按行输出到 loguru。
|
2026-04-09 11:54:30 +08:00
|
|
|
|
|
|
|
|
|
|
返回 CompletedProcess,stdout 为完整输出,便于失败时拼进异常信息。
|
2026-04-13 13:49:55 +08:00
|
|
|
|
``stream_to_logger=False`` 时不把子进程逐行写入日志(仍完整收集 stdout)。
|
2026-04-09 11:54:30 +08:00
|
|
|
|
"""
|
|
|
|
|
|
proc = subprocess.Popen(
|
|
|
|
|
|
cmd,
|
|
|
|
|
|
cwd=cwd,
|
|
|
|
|
|
env=env if env is not None else os.environ.copy(),
|
|
|
|
|
|
stdout=subprocess.PIPE,
|
|
|
|
|
|
stderr=subprocess.STDOUT,
|
|
|
|
|
|
text=True,
|
2026-04-16 11:38:30 +08:00
|
|
|
|
encoding="utf-8",
|
|
|
|
|
|
errors="replace",
|
2026-04-09 11:54:30 +08:00
|
|
|
|
bufsize=1,
|
|
|
|
|
|
)
|
|
|
|
|
|
lines: List[str] = []
|
|
|
|
|
|
if proc.stdout is not None:
|
|
|
|
|
|
for line in proc.stdout:
|
|
|
|
|
|
lines.append(line)
|
2026-04-13 13:49:55 +08:00
|
|
|
|
if stream_to_logger:
|
|
|
|
|
|
s = line.rstrip()
|
|
|
|
|
|
if s:
|
|
|
|
|
|
logger.info("[{}] {}", log_name, s)
|
2026-04-09 11:54:30 +08:00
|
|
|
|
rc = proc.wait()
|
|
|
|
|
|
out = "".join(lines)
|
|
|
|
|
|
return subprocess.CompletedProcess(cmd, rc, out, "")
|