代码之家  ›  专栏  ›  技术社区  ›  Gearoid Murphy

绕过用c或python中的popen缓冲子进程输出

  •  12
  • Gearoid Murphy  · 技术社区  · 15 年前

    我有一个关于popen(以及所有相关函数)的一般问题,适用于所有操作系统,当我编写一个python脚本或一些C代码并从控制台(win或linux)运行生成的可执行文件时,我可以立即看到进程的输出。但是,如果我运行与分叉进程相同的可执行文件,将其stdout重定向到管道中,则输出将缓冲在某个位置,通常最多4096字节,然后将其写入父进程可以读取的管道中。

    下面的python脚本将以1024字节为单位生成输出

    import os, sys, time
    
    if __name__ == "__main__":
         dye = '@'*1024
         for i in range (0,8):
            print dye
            time.sleep(1)
    

    下面的python脚本将执行前面的脚本,并在到达管道时立即读取输出,逐字节

    import os, sys, subprocess, time, thread
    
    if __name__ == "__main__":
        execArgs = ["c:\\python25\\python.exe", "C:\\Scripts\\PythonScratch\\byte_stream.py"]
    
        p = subprocess.Popen(execArgs, bufsize=0, stdout=subprocess.PIPE)
        while p.returncode == None:
            data = p.stdout.read(1)
            sys.stdout.write(data)
            p.poll()
    

    调整操作系统的路径。在这个配置中运行时,输出将不会以1024块显示,而是以4096块显示,尽管popen命令的缓冲区大小被设置为0(这仍然是默认值)。有人能告诉我如何改变这种行为吗?,是否有任何方法可以强制操作系统以与从控制台运行时相同的方式处理分叉进程的输出?也就是说,在不进行缓冲的情况下输入数据?

    2 回复  |  直到 13 年前
        1
  •  14
  •   Alex Martelli    15 年前

    一般来说,标准C运行时库(几乎代表每个系统上的每个程序运行,或多或少;-)检测stdout是否是终端;如果不是,它缓冲输出(与未缓冲的输出相比,这可能是一个巨大的效率胜利)。

    如果您控制了正在编写的程序,则可以(如另一个答案建议的那样)连续刷新stdout,或者(如果可行,更优雅地)尝试强制stdout不受缓冲,例如使用 -u 命令行标志:

    -u     : unbuffered binary stdout and stderr (also PYTHONUNBUFFERED=x)
             see man page for details on internal buffering relating to '-u'
    

    (该手册页添加的内容是提到stdin和二进制模式的问题)。

    如果你不能或不想接触正在编写的程序, -U 或者像在程序上那样,只是阅读不太可能有帮助(最重要的缓冲是发生在作者的stdout上的缓冲,而不是发生在读者的stdin上的缓冲)。另一种方法是欺骗作者相信它正在写入终端(即使实际上它正在写入另一个程序!),通过 pty 标准库模块或更高级别的第三方 pexpect 模块(或,对于Windows,其端口 wexpect )

        2
  •  1
  •   Havenard    15 年前

    这是正确的,适用于Windows和Linux(以及可能的其他系统),使用 popen() fopen() .如果要在4096字节之前调度输出缓冲区,请使用 fflush() (c)或 sys.stdout.flush() (蟒蛇)。