从子进程实时打印输出

时间:2013-11-06 17:23:46

标签: python subprocess real-time output stdout

我正在尝试为子进程实时打印stdout但是看起来stdout即使用bufsize = 0也是缓冲的,我无法弄清楚如何让它工作,我总是有延迟。

我尝试过的代码:

p = subprocess.Popen(cmd, 
                     stdout=subprocess.PIPE, 
                     stderr=subprocess.STDOUT, 
                     bufsize=0)
line = p.stdout.readline()
while line:
    sys.stdout.write(line)
    sys.stdout.flush()
    # DO OTHER STUFF
    line = p.stdout.readline()

还尝试使用for line in iter(p.stdout.readline, b'')代替while循环,使用read(1)代替readline()。总是相同的结果,输出会延迟很多秒或几分钟,并且会立即突然出现多条线。

我认为发生了什么:

bufsize设置为0(根据文档默认设置为0),因此管道传递给p.stdout的行应立即可用。但是,当管道换行时,p.stdout.readline()不会立即返回,这意味着它被缓冲,因此当缓冲区最终刷新到p.stdout时,会立即多行。

我该怎么做才能让它发挥作用?

3 个答案:

答案 0 :(得分:2)

感谢pobrelkey找到了问题的根源。实际上,延迟是由于孩子正在缓冲对stdout的写入这一事实,因为它没有写入tty。孩子使用stdio写入tty时是行缓冲的,否则它是完全缓冲的。

我设法使用pexpect代替subprocess让它工作。 pexpect使用伪tty,这正是我们需要的:

p = pexpect.spawn(cmd,args,timeout=None) 
line = p.readline() 
while line:
    sys.stdout.write(line)
    sys.stdout.flush()
    # DO OTHER STUFF
    line = p.readline()

在我的情况下甚至更好:

p = pexpect.spawn(cmd,args,timeout=None,logfile=sys.stdout)
line = p.readline() 
while line:
    # DO OTHER STUFF
    line = p.readline()

不再拖延!

关于pexpect的更多信息:wiki

答案 1 :(得分:0)

我首先要确保子进程本身不缓冲其输出。如果子进程又是Python程序,请继续执行下面的段落,了解如何禁用Python进程的输出缓冲。

根据Python,通常问题是Python默认缓冲stderr和stdout,即使你从代码中明确.flush()它。解决方案是在启动程序时将-u传递给Python。

此外,您可以执行for line in p.stdout而不是棘手的while循环。

P.S。实际上我尝试运行您的代码(使用cmd = ['cat', '/dev/urandom'])并且没有-u并且它已经实时输出了所有内容;这是在OS X 10.8上。

答案 2 :(得分:0)

如果您只是想让您的子进程的stdout转到您的stdout,为什么不让子进程从您的进程继承stdout?

subprocess.Popen(cmd, stdout=None, stderr=subprocess.STDOUT)