【问题标题】:How to get live output with subprocess in Python如何在 Python 中使用子进程获取实时输出
【发布时间】:2022-06-19 06:43:14
【问题描述】:

我正在尝试运行一个打印某些内容的 python 文件,等待 2 秒,然后再次打印。我想从我的 python 脚本中实时捕获这些输出,然后处理它们。我尝试了不同的方法,但没有任何效果。

process = subprocess.Popen(cmd, stdout=subprocess.PIPE)
while True:
    output = process.stdout.readline()
    if process.poll() is not None and output == '':
        break
    if output:
        print(output.strip())

我在这一点上,但它不起作用。它一直等到代码完成,然后打印所有输出。

我只需要运行一个 python 文件并从中获取实时输出,如果您有其他想法,不使用打印功能,请告诉我,只知道我必须单独运行该文件。我只是想到了最简单的方法,但是,据我所见,它无法完成。

【问题讨论】:

  • 这能回答你的问题吗? live output from subprocess command
  • 我都试过了,还是不行
  • 这里有三层缓冲,需要限制三层才能获取实时数据: 1) 使用stdbuf 或者改变程序本身,将程序的缓冲改为line面向模式(或添加fflushs);否则,一切都会卡在子进程的用户模式缓冲区中。 2)将bufsize=1 添加到Popen 参数(可能不需要,因为您不发送stdin,但无害)。 3) 将flush=True 添加到print 参数中(如果您连接到终端,行缓冲将为您刷新它,因此只有将标准输出通过管道传输到文件时才有意义)。跨度>
  • 明确一点,#1 中的stdbuf 是*NIX 世界的解决方案;您只需将Popen 更改为运行['stdbuf', '-oL'] + cmd
  • 谢谢@ShadowRanger,问题是打印上的flush=True。

标签: python subprocess stdout


【解决方案1】:

这里有三层缓冲,你需要限制这三层,以保证你得到实时数据:

  1. 使用stdbuf 命令(在 Linux 上)包装subprocess 执行(例如运行['stdbuf', '-oL'] + cmd 而不仅仅是cmd),或者(如果您有能力这样做)将程序本身更改为要么显式更改 stdout 上的缓冲(例如,使用 setvbuf 用于 C/C++ 代码将 stdout 全局切换到行缓冲模式,而不是输出到非 tty 时使用的默认块缓冲)或在关键输出之后插入刷新语句(例如,fflush(stdout); 用于 C/C++,fileobj.flush() 用于 Python 等)将程序缓冲到面向行的模式(或添加 fflushs);否则,一切都会卡在子进程的用户模式缓冲区中。

  2. bufsize=0 添加到Popen 参数(可能不需要,因为您不向标准输入发送任何内容,但无害),因此它会取消缓冲所有管道句柄。如果Popen 处于text=True 模式,请切换到bufsize=1(行缓冲,而不是无缓冲)。

  3. flush=True 添加到print 参数(如果您连接到终端,行缓冲将为您刷新它,因此只有将stdout 通过管道传输到文件时才有意义),或者显式调用sys.stdout.flush()

在这三者之间,您应该能够保证没有数据卡在用户模式缓冲区中等待;如果子流程至少已经输出了一行,它会立即到达您的手中,并且由它触发的任何输出也将立即出现。 Item #1 在大多数情况下是最难的(当你不能使用stdbuf,或者进程在内部重新配置了自己的缓冲并撤消了stdbuf 的效果,并且你不能修改进程可执行文件来修复它) ;您可以完全控制 #2 和 #3,但 #1 可能不在您的控制范围内。

【讨论】:

    【解决方案2】:

    这是我用于相同目的的代码:

    def run_command(command, **kwargs):
        """Run a command while printing the live output"""
        process = subprocess.Popen(
            command,
            stdout=subprocess.PIPE,
            stderr=subprocess.STDOUT,
            **kwargs,
        )
        while True:   # Could be more pythonic with := in Python3.8+
            line = process.stdout.readline()
            if not line and process.poll() is not None:
                break
            print(line.decode(), end='')
    

    使用示例如下:

    run_command(['git', 'status'], cwd=Path(__file__).parent.absolute())
    

    【讨论】:

      猜你喜欢
      • 2021-01-18
      • 2015-06-08
      • 2010-10-22
      • 1970-01-01
      • 2019-01-25
      • 2013-11-26
      相关资源
      最近更新 更多