可以拆分/合并subprocess.Popen的输出流吗?

7 投票
1 回答
1382 浏览
提问于 2025-04-17 10:16

我正在为一个工作流管理器写一个包装类。我想以特定的方式记录一个应用程序的输出(这个应用程序是通过 subprocess.Popen 执行的子进程):

  • 子进程的 stdout 输出应该同时写入一个日志文件和父进程的 stdout
  • 子进程的 stderr 输出应该写入另一个日志文件,但也要输出到父进程的 stdout

也就是说,所有来自子进程的输出都应该合并到 stdout 中(就像使用 subprocess.Popen(..., stderr=subprocess.STDOUT) 一样),这样我就可以把 stderr 留给包装类本身的日志消息。另一方面,子进程的输出流应该写入不同的文件,以便进行单独的验证。

我尝试使用一个“分流器”辅助类,将两个流(stdout 和日志文件)连接在一起,这样 Tee.write 就可以同时写入这两个流。然而,这个分流器不能传递给 Popen,因为“subprocess”使用的是操作系统级别的写入函数(具体可以查看这里:http://bugs.python.org/issue1631)。

我当前解决方案的问题是(下面的代码片段,主要改编自 这里),就是 stdout 上的输出可能不会按正确的顺序出现。

我该如何克服这个问题?或者我应该使用完全不同的方法?(如果我坚持使用下面的代码,如何选择 os.read 中字节数的值?)

import subprocess, select, sys, os

call = ... # set this
process = subprocess.Popen(call, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
logs = {process.stdout: open("out.log", "w"), process.stderr: open("err.log", "w")}
done = {process.stdout: False, process.stderr: False}
while (process.poll() is None) or (not all(done.values())):
    ready = select.select([process.stdout, process.stderr], [], [])[0]
    for stream in ready:
        data = os.read(stream.fileno(), 1)
        if data:
            sys.stdout.write(data)
            logs[stream].write(data)
        else:
            done[stream] = True
logs[process.stdout].close()
logs[process.stderr].close()

顺便说一下,这个使用 "fcntl" 的解决方案对我没有用。而且我还没弄明白如何将 这个解决方案 适应到我的情况,所以我还没有尝试过。

1 个回答

1

如果你把 shell=True 设置上,那么你就可以给 subprocess 传一个包含管道、重定向和 tee 命令 的命令字符串。

撰写回答