4

目前,我有这样的事情:

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

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

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

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

谢谢!

4

2 回答 2

5

我能想到几个解决方案。

#1:你可以直接进入源代码communicate,复制并粘贴它,添加代码来打印每一行,以及缓冲。(如果你自己stdout的阻塞可能是因为,比如说,一个死锁的父母,你可以使用 athreading.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: 或者你可以创建一个包装类,它接受一个文件对象并在每次有人从它时发送到stdout/ 。然后手动创建管道,并传入包裹的管道,而不是使用 automagic 。这与 #1 有完全相同的问题(意味着没有问题,或者如果可以阻止,您需要使用 a或其他东西)。stderrreadPIPEQueuesys.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会被AttributeException寻找并明确编码那些,或者做通常的__getattr__技巧来委托一切。PS,如果您担心这个“文件对象”的概念意味着磁盘存储,请阅读Wikipedia 上的 Everything is a file

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

于 2013-03-27T08:10:11.153 回答
2

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

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

于 2013-03-27T08:08:05.427 回答