让子进程同时输出到文件和标准输出/标准错误的最简单界面是什么?

11 浏览
0 Comments

让子进程同时输出到文件和标准输出/标准错误的最简单界面是什么?

我希望在不调用`tee`的情况下,在Python的subprocess中实现与`cmd > >(tee -a {{ out.log }}) 2> >(tee -a {{ err.log }} >&2)`类似的效果。基本上,将stdout同时写入stdout和out.log文件,并将stderr同时写入stderr和err.log文件。我知道我可以使用一个循环来处理它,参考链接:loop。但是,由于我的代码中已经有很多Popen和subprocess.run调用,我不想重写整个代码,我想知道是否有某个包提供了更简单的接口,可以让我像这样做:

subprocess.run(["ls", "-l"], stdout=some_magic_file_object(sys.stdout, 'out.log'), stderr=some_magic_file_object(sys.stderr, 'out.log'))

0
0 Comments

问题的出现原因:没有简单的方法可以让子进程同时输出到文件和stdout/stderr。

解决方法:可以使用一个名为Tee的类来实现。该类接受一个文件对象列表作为参数,并创建一个子进程来读取自己的fileno(即subprocess.run将写入的内容),并将该内容写入提供的所有文件中。需要进行一些生命周期管理,关闭文件描述符并等待子进程结束。可以手动调用Tee对象的close方法进行关闭,或者使用它作为上下文管理器。

使用示例代码如下:

import subprocess
import sys
logfile = open('out.log', 'w')
stdout_magic_file_object = Tee(sys.stdout, logfile)
stderr_magic_file_object = Tee(sys.stderr, logfile)
# 使用文件对象进行多个子进程调用
subprocess.run(["ls", "-l"], stdout=stdout_magic_file_object, stderr=stderr_magic_file_object)
# 使用完文件对象后关闭
stdout_magic_file_object.close()
stderr_magic_file_object.close()
logfile.close()

更清晰的方式是使用上下文管理器,如下所示。这需要进行更多的重构,所以您可能更喜欢手动关闭文件。

import subprocess
import sys
with open('out.log', 'w') as logfile:
    with Tee(sys.stdout, logfile) as stdout, Tee(sys.stderr, logfile) as stderr:
        subprocess.run(["ls", "-l"], stdout=stdout, stderr=stderr)

该方法的一个问题是子进程立即写入stdout,因此Python自身的输出经常会与其混合在一起。可以通过在临时文件和日志文件上使用Tee,然后在退出Tee上下文块时打印临时文件的内容(并删除它)来解决此问题。可以很容易地创建一个自动执行此操作的Tee子类,但是使用起来可能会有些麻烦,因为现在需要退出上下文块(或以其他方式运行一些代码)才能打印出子进程的输出。

对于该方法的一个问题是,子进程会立即写入stdout,因此Python的输出会经常与其混合。可以通过在临时文件和日志文件上使用Tee,然后在退出Tee上下文块时打印临时文件的内容(并删除它)来解决此问题。可以很容易地创建一个自动执行此操作的Tee子类,但是使用起来可能会有些麻烦,因为现在需要退出上下文块(或以其他方式运行一些代码)才能打印出子进程的输出。

感谢您的帮助!实际上,我希望尽快看到输出,因此即使立即写出也不是问题。但是,当进程输出大量数据时,这种方法似乎存在性能问题,甚至在极端情况下可能会锁定。它使用一个Python循环每次读取1个字节。我猜我们必须实现类似subprocess.PIPE的东西,具有可配置的缓冲区大小(也许只需使用io.open())并在有机会时从缓冲区读取所有字节。

最初我一次处理一个字节以提供即时反馈,而不是等到流结束后输出缓冲区中的数据。我为Tee添加了一个可以设置更大(例如4096)的bufsize关键字参数。这将导致较少的即时反馈,因为在流结束之前,最后4095个字节不会被输出,但吞吐量应该更高。不确定会导致它锁定的确切原因。如果有更多细节,也许我可以改进解决方案。

0