【问题标题】:How to capture streaming output in python from subprocess.communicate()如何从 subprocess.communicate() 在 python 中捕获流输出
【发布时间】:2013-03-17 06:10:39
【问题描述】:

目前,我有这样的事情:

self.process = subprocess.Popen(self.cmd, stdout=subprocess.PIPE)
out, err = self.process.communicate()

我正在运行的命令流式传输输出,我需要在继续之前阻止该进程。

如何才能捕获流输出并通过标准输出打印流输出?当我设置stdout=subprocess.PIPE 时,我可以捕获输出,但它不会打印输出。如果我忽略stdout=subprocess.PIPE,它会打印输出,但communicate() 将返回None

是否有一种解决方案可以满足我的要求,同时提供阻塞直到进程终止/完成,并避免提到here 的缓冲区问题和管道死锁问题?

谢谢!

【问题讨论】:

    标签: python subprocess popen


    【解决方案1】:

    输出到您的调用进程,本质上是从self.cmd 捕获stdout,因此输出不会到其他任何地方。

    如果您想查看输出,您需要做的是从“父”进程中打印出来。

    【讨论】:

    • 我很确定关键是他想在它进来时打印它——但他的父进程被阻止直到communicate 返回,因此他不能从父进程。
    【解决方案2】:

    我能想到几个解决方案。

    #1:您可以直接进入源代码获取the code for communicate,复制并粘贴它,添加代码以打印每一行,并进行缓冲。 (如果你自己的stdout 可能因为一个死锁的父级而阻塞,你可以使用threading.Queue 或其他东西。)这显然有点hacky,但它很容易,而且很安全。

    但实际上,communicate 很复杂,因为它需要完全通用,并处理您不需要的情况。您需要的只是核心技巧:在问题上抛出线程。您只需要一个不会在read 调用之间造成任何缓慢或阻塞的专用读取器线程。

    类似这样的:

    self.process = subprocess.Popen(self.cmd, stdout=subprocess.PIPE)
    lines = []
    def reader():
        for line in self.process.stdout:
            lines.append(line)
            sys.stdout.write(line)
    t = threading.Thread(target=reader)
    t.start()
    self.process.wait()
    t.join()
    

    您可能需要在reader 线程中进行一些错误处理。而且我不是 100% 确定您可以在这里安全地使用readline。但这要么有效,要么接近。

    #2: 或者你可以创建一个包装类,它接收一个文件对象并在每次有人从它reads 时发送到stdout/stderr。然后手动创建管道,并传入包裹的管道,而不是使用自动魔术PIPE。这与 #1 有完全相同的问题(意味着没有问题,或者您需要使用 Queuesys.stdout.write 可以阻止的东西)。

    类似这样的:

    class TeeReader(object):
        def __init__(self, input_file, tee_file):
            self.input_file = input_file
            self.tee_file = tee_file
        def read(self, size=-1):
            ret = self.input_file.read(size)
            if ret:
                self.tee_file.write(ret)
            return ret
    

    换句话说,它包装了一个文件对象(或类似文件的东西),并像一个文件对象一样工作。 (当您使用PIPE 时,process.stdout 在 Unix 上是一个真实的文件对象,但可能只是在 Windows 上的行为。)您需要委托给 input_file 的任何其他方法都可以直接委托,无需任何额外的包装。试试这个,看看communicate 得到什么方法AttributeExceptions 寻找并明确编码这些方法,或者使用通常的__getattr__ 技巧来委派所有内容。 PS,如果你担心这个“文件对象”的概念意味着磁盘存储,请阅读维基百科上的Everything is a file

    #3:最后,您可以获取 PyPI 上的“异步子进程”模块之一,或者包含在 twisted 或其他异步框架中并使用它。 (这使得 可能 避免死锁问题,但这不是保证 - 您仍然必须确保正确地服务管道。)

    【讨论】:

    • 您将如何实现#2? 'file' 对象是否表明我必须写入磁盘(我不想这样做)?
    • @jaka:不,管道已经是一个文件对象。您正在创建一个包装文件对象的对象,并将所有文件方法委托给它(read 除外,它围绕委托调用做了一些额外的工作)。
    • 我会尝试在前两个答案中至少添加一些骨架代码。
    • 我认为线程是我需要的。谢谢!一些骨架代码也会很棒!再次感谢!
    • 感谢您的代码。只是一个简单的问题——如果我用out, err = self.process.communicate() 替换self.process.wait(),为什么out 没有输出? reader 线程真的会影响communicate()吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-02-12
    • 1970-01-01
    • 2014-09-11
    相关资源
    最近更新 更多