2

我正在尝试为子进程实时打印标准输出,但看起来标准输出即使在 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()

还尝试了 withfor line in iter(p.stdout.readline, b'')而不是 while 循环和 withread(1)而不是readline(). 总是相同的结果,输出会延迟很多秒或几分钟,并且会突然出现多行。

我认为会发生什么:

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

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

4

3 回答 3

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

于 2013-11-07T14:05:41.353 回答
0

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

根据 Python,通常问题是 Python 默认会缓冲 stderr 和 stdout,即使您.flush()从代码中显式显示它也是如此。-u解决方案是在启动程序时传递给 Python。

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

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

于 2013-11-06T17:28:28.960 回答
0

如果您只想让子进程的标准输出进入您的标准输出,为什么不让子进程从您的进程继承标准输出呢?

subprocess.Popen(cmd, stdout=None, stderr=subprocess.STDOUT) 
于 2013-11-06T17:30:18.463 回答