为什么 subprocess 的标准输出(重定向到非缓冲文件)会被缓冲?

11

来自http://docs.python.org/library/functions.html#open

可选的缓冲区参数bufsize表示文件期望的缓冲区大小:0表示不带缓冲,1表示行缓冲,任何其他正值表示使用大约该大小的缓冲区。负数的缓冲区大小意味着使用系统默认值,通常是对于tty设备进行行缓冲,对于其他文件进行完全缓冲。如果省略,则使用系统默认值。

我在下面使用了0作为bufsize参数,但运行main_process时没有使用flush()而没有输出写入文件。
这是什么原因?

# --------------------------------- sub_process.py
import sys
import time

if __name__ == '__main__':
    print 'printed from redirect.py'
    # why is the following flush() needed? 'std-output' is (?) unbuffered...
    sys.stdout.flush() 
    time.sleep(6)


# --------------------------------- main_process.py
import subprocess
import time

if __name__ == '__main__':
    p = subprocess.Popen(
        ['python', 'sub_process.py'],
        stdout=open('std-output', 'w', 0))
    time.sleep(3)
    p.terminate()

+1,我前几天花了大约30分钟的时间试图弄清楚为什么 sys.stdout -> subprocess.PIPE 不起作用。flush() 是答案,但我们为什么需要它呢? - Mike Pennington
2个回答

8
使用带有 -u 标志的 Python,例如:
if __name__ == '__main__':
    p = subprocess.Popen(
        ['python', '-u', 'sub_process.py'],
        stdout=open('std-output', 'w'))
    time.sleep(3)
    p.terminate()

这个可行,谢谢。但是,我仍然想知道为什么需要它? - Piotr Dobrogost
1
我的猜测是,如果你使用file.write()直接向文件写入数据,那么它将不会被缓存。但实际上,你是间接地向文件写入数据,因为Python会在发送数据到文件之前缓存你的print命令,因此你需要使用-u标志告诉Python不要缓存print命令。 - ralphtheninja
sys.stdout.write('printed from redirect.py')与print语句的结果相同 - 没有内容被写入文件。 - Piotr Dobrogost

5

扩展Magnus Skog的解决方案(顺便加一分:):

基本上,当子进程fork出一个新进程时,它将使用os.dup2(请参见subprocess.Popen._execute_child)将stdout参数复制到新的子进程stdout(fileno = 1),并保持未缓冲状态(如dup2所做的)。到目前为止,一切都好,但是当Python在子进程中启动时,默认情况下,如果Python没有看到-u标志,则会将stdout的缓冲区设置为行缓冲区(请参见主要Python函数),这将覆盖您之前设置的缓冲标志。

希望这更能解释您所看到的行为。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接