【问题标题】:Python - capture Popen stdout AND display on console?Python - 捕获 Popen 标准输出并在控制台上显示?
【发布时间】:2010-11-19 23:06:59
【问题描述】:

我想从通过subprocess.Popen(...) 启动的长时间运行的进程中捕获标准输出,所以我使用stdout=PIPE 作为参数。

但是,因为这是一个长时间运行的过程,我还想将输出发送到控制台(好像我没有通过管道传输它一样),让脚本的用户知道它仍在工作。

这可能吗?

干杯。

【问题讨论】:

标签: python pipe popen


【解决方案1】:

或者,您可以将流程通过管道传输到 tee 并仅捕获其中一个流。 类似于sh -c 'process interesting stuff' | tee /dev/stderr

当然,这只适用于类 Unix 系统。

【讨论】:

  • 不幸的是仍然受到缓冲引起的抖动:-(.
【解决方案2】:

受上面某处 pty.openpty() 建议的启发,在 python2.6、linux 上测试。发布,因为它需要一段时间才能使其正常工作,没有缓冲......

def call_and_peek_output(cmd, shell=False):
    import pty, subprocess
    master, slave = pty.openpty()
    p = subprocess.Popen(cmd, shell=shell, stdin=None, stdout=slave, close_fds=True)
    os.close(slave)
    line = ""
    while True:
        try:
            ch = os.read(master, 1)
        except OSError:
            # We get this exception when the spawn process closes all references to the
            # pty descriptor which we passed him to use for stdout
            # (typically when it and its childs exit)
            break
        line += ch
        sys.stdout.write(ch)
        if ch == '\n':
            yield line
            line = ""
    if line:
        yield line

    ret = p.wait()
    if ret:
        raise subprocess.CalledProcessError(ret, cmd)

for l in call_and_peek_output("ls /", shell=True):
    pass

【讨论】:

    【解决方案3】:

    S。洛特的评论指向Getting realtime output using subprocessReal-time intercepting of stdout from another process in Python

    我很好奇亚历克斯在这里的答案与他的答案 1085071 不同。 我对其他两个参考问题的答案进行了简单的小实验,结果很好......

    按照上面 Alex 的回答,我去看了 wexpect,但我不得不说,阅读代码中的 cmets,我对使用它并没有很好的感觉。

    我想这里的元问题是 pexpect/wexpect 什么时候会成为包含的电池之一?

    【讨论】:

      【解决方案4】:

      缓冲您的长时间运行的子进程可能会导致您的控制台输出不稳定和非常糟糕的用户体验。我建议您考虑改用pexpect(或者,在Windows 上,wexpect)来消除这种缓冲并从子进程中获得平滑、常规的输出。例如(在几乎任何 unix-y 系统上,安装 pexpect 之后):

      >>> import pexpect
      >>> child = pexpect.spawn('/bin/bash -c "echo ba; sleep 1; echo bu"', logfile=sys.stdout); x=child.expect(pexpect.EOF); child.close()
      ba
      bu
      >>> child.before
      'ba\r\nbu\r\n'
      

      ba 和 bu 将在适当的时间出现(它们之间大约一秒钟)。请注意,输出不受正常终端处理的影响,因此回车符保留在那里 - 如果您需要 \n 作为结尾,您需要自己对字符串进行后处理(只是一个简单的.replace!-) -of-line 标记(缺少处理很重要,以防子进程将二进制数据写入其标准输出 - 这可确保所有数据完好无损!-)。

      【讨论】:

        【解决方案5】:

        当你从管道中读取它时,你能简单地 print 它吗?

        【讨论】:

        • 啊! (灯泡时刻!) - p.stdout 是一个类似对象的文件。所以我只需要循环调用 readline() 吗?正确的?退出循环的最佳方法是什么 - 当 p.stdout 返回空字符串时?为了确定,我还应该做 p.wait() 吗?
        • @Steve:读取 p.stdout 返回的空字符串意味着子级已关闭其标准输出,但不一定是已终止。 p.wait() 将等待它终止。
        猜你喜欢
        • 2010-10-21
        • 1970-01-01
        • 2012-09-09
        • 2014-08-15
        • 1970-01-01
        • 1970-01-01
        • 2014-11-28
        • 1970-01-01
        相关资源
        最近更新 更多