在popen.stdout.readline上检测流的结尾

Leo*_*opd 5 python popen python-2.7

我有一个python程序,它使用Popen并在生成时几乎实时地使用它们的输出来启动子进程.相关循环的代码是:

def run(self, output_consumer):
    self.prepare_to_run()
    popen_args = self.get_popen_args()
    logging.debug("Calling popen with arguments %s" % popen_args)
    self.popen = subprocess.Popen(**popen_args)
    while True:
        outdata = self.popen.stdout.readline()
        if not outdata and self.popen.returncode is not None:
            # Terminate when we've read all the output and the returncode is set
            break
        output_consumer.process_output(outdata)
        self.popen.poll()  # updates returncode so we can exit the loop
    output_consumer.finish(self.popen.returncode)
    self.post_run()

def get_popen_args(self):
    return {
        'args': self.command,
        'shell': False, # Just being explicit for security's sake
        'bufsize': 0,   # More likely to see what's being printed as it happens
                        # Not guarantted since the process itself might buffer its output
                        # run `python -u` to unbuffer output of a python processes
        'cwd': self.get_cwd(),
        'env': self.get_environment(),
        'stdout': subprocess.PIPE,
        'stderr': subprocess.STDOUT,
        'close_fds': True,  # Doesn't seem to matter
    }
Run Code Online (Sandbox Code Playgroud)

这在我的生产机器上运行良好,但在我的开发机器上,.readline()当某些子进程完成时,调用挂起.也就是说,它将成功处理所有输出,包括最后输出行说"过程完成",但随后将再次轮询readline并且永不返回.对于我调用的大多数子进程,此方法在dev机器上正确退出,但始终无法退出一个本身调用许多子进程的复杂bash脚本.

值得注意的是,在输出结束之前将许多行popen.returncode设置为非None(通常0)值.所以我不能在设置它时突然退出循环,否则我会失去在进程结束时吐出来的所有东西,并且仍然在缓冲等待阅读.问题是,当我在那个时候刷新缓冲区时,我无法判断我到底是什么时候因为最后一次readline()挂断电话.打电话read()也挂了.调用read(1)让我每个最后一个角色都出来,但也会在最后一行之后挂起. popen.stdout.closed永远False.我怎么知道我什么时候结束?

所有系统都在Ubuntu 12.04LTS上运行python 2.7.3.FWIW stderr正在与stdout使用合并stderr=subprocess.STDOUT.

为什么不同?它是否由于stdout某种原因未能关闭?子流程可以做些什么来保持它以某种方式打开吗?可能是因为我从开发盒上的终端启动了这个过程,但是在生产中它是作为守护进程启动的supervisord吗?这会改变管道的处理方式吗?如果是这样,我该如何规范它们?

muu*_*ope 3

主代码循环看起来是正确的。管道可能没有关闭,因为另一个进程使其保持打开状态。例如,如果脚本启动一个写入的后台进程stdout,则管道将不会关闭。您确定没有其他子进程仍在运行吗?

一个想法是当您看到已设置时更改模式.returncode。一旦您知道主进程已完成,请从缓冲区读取其所有输出,但不要陷入等待。您可以使用select从管道中读取超时。设置几秒钟的超时,您可以清除缓冲区,而不会卡在等待子进程中。