我正在尝试为子进程实时打印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
时,会立即多行。
我该怎么做才能让它发挥作用?
答案 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)