Popen在可用时不立即提供输出

时间:2012-04-01 11:55:01

标签: python subprocess popen

我正在尝试从stdout读取stderrPopen并将其打印出来。我与Popen一起运行的命令如下

#!/bin/bash

i=10
while (( i > 0 )); do
    sleep 1s
    echo heyo-$i
    i="$((i-1))"
done

echo 'to error' >&2

当我在shell中运行它时,我获得一行输出然后第二次中断然后再一行,等等。但是,我无法使用python重新创建它。我正在启动两个线程,每个线程从stdoutstderr读取,将读取的行放入Queue,另一个线程从此队列中取出项目并打印出来。但有了这个,我看到在子进程结束后,所有输出都会立即打印出来。我想要在echo'编辑时打印这些行。

这是我的python代码:

# The `randoms` is in the $PATH
proc = sp.Popen(['randoms'], stdout=sp.PIPE, stderr=sp.PIPE, bufsize=0)

q = Queue()

def stream_watcher(stream, name=None):
    """Take lines from the stream and put them in the q"""
    for line in stream:
        q.put((name, line))
    if not stream.closed:
        stream.close()

Thread(target=stream_watcher, args=(proc.stdout, 'out')).start()
Thread(target=stream_watcher, args=(proc.stderr, 'err')).start()

def displayer():
    """Take lines from the q and add them to the display"""
    while True:
        try:
            name, line = q.get(True, 1)
        except Empty:
            if proc.poll() is not None:
                break
        else:
            # Print line with the trailing newline character
            print(name.upper(), '->', line[:-1])
            q.task_done()

    print('-*- FINISHED -*-')

Thread(target=displayer).start()

有什么想法吗?我在这里缺少什么?

2 个答案:

答案 0 :(得分:4)

只有stderr是无缓冲的,而不是stdout。你想要的只能使用shell内置函数来完成。缓冲行为在 stdio (3)C库中定义,该库仅在输出到终端时才应用行缓冲。当输出到管道时,它是管道缓冲的,而不是行缓冲的,因此数据不会传输到内核,然后传输到管道的另一端,直到管道缓冲区填满。

此外,shell无法访问libc的缓冲区控制功能,例如 setbuf (3)和朋友。 shell中唯一可行的解​​决方案是在伪tty上启动协同进程,而pty管理是一个复杂的主题。使用一种语言重写等效的shell脚本要容易得多,该语言为输出流授予对低级缓冲功能的访问权限,而不是安排在pty上运行某些东西。

但是,如果您拨打/bin/echo而不是内置echo的shell,您可能会发现它更符合您的喜好。这是有效的,因为现在每次新启动的/bin/echo进程终止时都会刷新整行。这不是对系统资源的有效利用,但可能是您自己的有效使用。

答案 1 :(得分:-1)

IIRC,在Popen上设置shell=True应该这样做。