如何从subprocess.communicate()捕获python中的流输出

时间:2013-03-27 08:04:56

标签: python subprocess popen

目前,我有这样的事情:

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

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

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

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

谢谢!

2 个答案:

答案 0 :(得分:4)

我可以想到一些解决方案。

#1:你可以直接进入源代码来抓取the code for communicate,复制并粘贴它,添加代码,打印出来的每一行以及缓冲内容。 (如果你自己的stdout因为一个死锁的父母而被阻止,你可以使用threading.Queue或者其他东西。)这显然有些笨拙,但这很容易,而且会安全的。

但实际上,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:或者你可以创建一个包装器类,每当有stdout个人stderr时,它就会将文件对象和tees带到read / PIPE。然后手动创建管道,并传入包裹的管道,而不是使用自动Queue。这与#1完全相同(意味着没有问题,或者如果sys.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时,input_file是Unix上的真实文件对象,但可能就像在Windows上一样。)您需要委派给communicate的任何其他方法都可以可能是直接委派,没有任何额外的包装。要么尝试这个,看看AttributeException获取__getattr__的方法是什么,并明确地对这些方法进行编码,或者执行通常的twisted技巧来委派所有内容。 PS,如果你担心这个“文件对象”的想法意味着磁盘存储,请在维基百科上阅读Everything is a file

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

答案 1 :(得分:1)

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

您需要做的是从父母那里打印出来。如果你想看到输出,请进行处理。