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

在C或Python中使用popen绕过子进程输出的缓冲

  •  13
  • Gearoid Murphy  · 技术社区  · 17 年前

    我对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()
    

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

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

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

    如果你控制着正在编写的程序,你可以(正如另一个答案所建议的那样)连续刷新stdout,或者(如果可行的话,可以更优雅地)尝试强制stdout无缓冲,例如通过使用以下命令运行Python -u 命令行标志:

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

    如果你不能或不想触摸正在编写的程序, 在只进行读取的程序上执行类似操作不太可能有帮助(最重要的缓冲是发生在编写器的stdout上的缓冲,而不是发生在读取器的stdin上的缓冲)。另一种方法是通过以下方式欺骗作者相信它正在向终端写入(即使事实上它正在向另一个程序写入!) pty 标准库模块或更高级别的第三方 pexpect 模块(或者,对于Windows,其端口 wexpect ).

        2
  •  1
  •   Havenard    17 年前

    这是正确的,适用于Windows和Linux(可能还有其他系统) popen() 和 fopen() 。如果要在4096字节之前分派输出缓冲区,请使用 fflush() (C)或 sys.stdout.flush() python