无缓冲子进程输出(最后一行缺失)

3 投票
2 回答
2722 浏览
提问于 2025-04-15 20:20

我可能忽略了一些非常明显的东西。我需要运行一个C语言程序,实时显示它的输出,最后解析它的最后一行,这应该很简单,因为最后打印的那一行总是一样的。

process = subprocess.Popen(args, shell = True,  
                           stdout = subprocess.PIPE, stderr = subprocess.PIPE)

# None indicates that the process hasn't terminated yet.
while process.poll() is None:

    # Always save the last non-emtpy line that was output by the child
    # process, as it will write an empty line when closing its stdout.
    out = process.stdout.readline()
    if out:
        last_non_empty_line = out

    if verbose:
        sys.stdout.write(out)   
        sys.stdout.flush()

# Parse 'out' here...

不过,有时候最后一行并没有被打印出来。Popens的默认bufsize值是0,所以它应该是没有缓存的。我也尝试过在C代码退出前加上fflush(stdout),但似乎在程序退出前完全没有必要刷新输出流。

有没有人有什么想法?

2 个回答

3

问题在于,你一直在读取输出的每一行,直到这个进程结束(通过process.poll()来检查)。不过,由于你使用了一个特定的选项,实际上是有缓存的。

你需要继续读取进程的标准输出,直到到达文件的末尾或者遇到空行为止。

2

readline() 这个函数需要先把文本缓存起来,等到遇到换行符才会处理。

你总是会遇到竞争条件——无论你用多少不带缓存的流,都无法解决这样一个问题:你在处理一行数据,然后检查是否退出,再读取一行数据。如果在你处理这一行的时候,子进程已经退出了,那你就不会再读取到其他内容了。而且,命令行本身可能也在引入自己的缓存。

所以你可以选择:

  1. 使用 communicate(),这样在子进程运行时就不会有详细的输出。

  2. 确保在进程退出后继续读取,直到你遇到文件结束符(EOF)。

我还建议你修改代码,这样就不需要使用 shell=True

撰写回答