【问题标题】:How to get subprocess' stdout data asynchronously?如何异步获取子进程的标准输出数据?
【发布时间】:2012-02-17 03:49:31
【问题描述】:

我为我的应用程序编写了一个简单的 Python 脚本,并预定义了一些快速命令,例如 make 等。

我写了一个运行系统命令的函数(linux):

def runCommand(commandLine):
    print('############## Running command: ' + commandLine)
    p = subprocess.Popen(commandLine, shell = True, stdout = subprocess.PIPE)
    print (p.stdout.read().decode('utf-8'))

除了一些事情之外,一切都很好:

  • 我正在使用 cmake,它的输出是彩色的。有机会在输出中保存颜色吗?

  • 我可以在进程完成后查看输出。例如,make 运行了很长时间,但我只能在完全编译后才能看到输出。怎么异步呢?

【问题讨论】:

    标签: python asynchronous stdout subprocess


    【解决方案1】:

    我不确定颜色,但这里是如何一次轮询子进程的标准输出一行:

    import subprocess
    proc = subprocess.Popen('cmake', shell=True, stdout=subprocess.PIPE)
    while proc.poll() is None:
        output = proc.stdout.readline()
        print output
    

    不要忘记从 stderr 读取,因为我确信 cmake 会在那里发出信息。

    【讨论】:

      【解决方案2】:

      你没有得到颜色,因为cmake 检测它的标准输出是否是终端,如果不是,它不会为自己的输出着色。某些程序为您提供强制着色输出的选项。不幸的是cmake 没有,所以你不走运。除非你想自己修补cmake

      很多程序都这样做,例如 grep:

      # grep test test.txt
      test
       ^
       |
       |------- this word is red
      

      现在通过管道将它传递给 cat:

      # grep test test.txt | cat
      test
       ^
       |
       |------- no longer red
      

      grep 选项--color=always 强制颜色:

      # grep test test.txt --color=always | cat
      test
       ^
       |
       |------- red again
      

      【讨论】:

        【解决方案3】:

        关于如何在完成之前获取进程的输出,应该可以替换:

        p.stdout.read
        

        与:

        for line in p.stdout:
        

        关于如何保存彩色输出,没有什么特别之处。例如,如果将行输出保存到文件中,则下次执行 cat <logfile> 时,控制台将解释转义序列并按预期显示颜色。

        【讨论】:

        • with readline() 我在一行中有一个数字而不是文本,怎么了?
        • @Ockonal 其实如果你使用for循环,就没有必要使用readline。您可以找到两种策略的示例here
        • 根据stackoverflow.com/questions/2804543/…,其实for line in p.stdout在Python 2中不好用
        【解决方案4】:

        对于 CMake,您可以使用选项 CLICOLOR_FORCE=1 强制颜色输出:

        command = 'make CLICOLOR_FORCE=1'
        args = shlex.split(command)
        proc = subprocess.Popen(args, stdout=subprocess.PIPE)
        

        然后像accepted answer一样打印:

        while proc.poll() is None:
            output = proc.stdout.readline()
            print(output.decode('utf-8'))
        

        如果您在打印前解码为 utf-8,您应该会看到彩色输出。 如果您将结果打印为字节文字(即不解码),您应该会看到各种颜色的转义序列。

        考虑尝试选项universal_newlines=True

        proc = subprocess.Popen(args, stdout=subprocess.PIPE, universal_newlines=True)
        

        这会导致对 proc.stdout.readline() 的调用返回字符串而不是字节文字,因此您可以/必须跳过对 decode() 的调用。

        【讨论】:

          【解决方案5】:

          要进行异步输出,请执行以下操作:http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/440554

          不确定是否可以捕获彩色输出。如果你能得到转义的颜色代码,你也许可以。

          【讨论】:

          • 如果我们得到颜色代码,终端会翻译它们。但问题是,大多数应用程序区分连接到终端或通过管道传输到另一个应用程序。说服这些应用程序仍然生成颜色代码的唯一方法是假装接收应用程序(在本例中是我们的 python 进程)实际上是一个 tty 而不是管道。说起来容易做起来难……
          【解决方案6】:

          这里值得注意的是使用script 命令作为伪终端并被检测为tty 而不是redirect(pipe) 文件描述符,请参见: bash command preserve color when piping

          像魅力一样工作......

          根据问题中的示例,只需让script 执行cmake

          import subprocess
          proc = subprocess.Popen('script cmake', shell=True, stdout=subprocess.PIPE)
          while proc.poll() is None:
              output = proc.stdout.readline()
              print output
          

          这使cmake 误以为它是从终端执行的,并会产生您想要的 ANSI 糖果。

          快乐!

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2010-10-29
            • 2016-11-17
            • 1970-01-01
            • 1970-01-01
            • 2020-03-03
            • 2014-04-29
            • 2019-03-23
            • 1970-01-01
            相关资源
            最近更新 更多