Python子进程捕获输出

本文的主要目的是演示如何在Python中捕获、存储和显示一个Subprocess 的输出。

Python子进程捕获输出

Subprocess 是一个内置的Python模块,预先安装在Python的安装文件中。它用于通过创建新的进程来运行新的代码和应用程序,其中给定的任务(根据所传递的内容)与它的进程分开执行。

使用Subprocess 的主要优点之一是,它允许我们从当前编写的程序中启动新的程序。

运行这种程序的一个例子是,如果你想从Git仓库克隆一些项目,或者也许执行一个C++编译器二进制文件,执行其特定的任务。

考虑我们要执行一个用C++编写的程序,其代码如下。

#include <iostream>using namespace std;
int main()
{
  int rows = 5, count = 0, count1 = 0, k = 0;
  for(int i = 1; i <= rows; ++i)
  {
    for(int space = 1; space <= rows-i; ++space)
    {
      cout << "  ";
      ++count;
    }
  while(k != 2*i-1)
  {
    if (count <= rows-1)
    {
      cout << i+k << " ";
      ++count;
    }
    else
    {
      ++count1;
      cout << i+k-2*count1 << " ";
    }
    ++k;
  }
  count1 = count = k = 0;
  cout << endl;
  }
return 0;
}

其中独立打开执行,会有如下输出:

Python子进程捕获输出

现在我们执行用Python编写的程序的编译二进制文件。

import subprocess
p2 = subprocess.Popen("program\pyramid.exe")
print(p2)

输出:

<Popen: returncode: None args: '....\program\pyramid.exe>
        1
      2 3 2
    3 4 5 4 3
  4 5 6 7 6 5 4
5 6 7 8 9 8 7 6 5

从上面的代码中可以看出,很明显,我们没有得到字符串,而是得到了Popen 的实例,这并不是理想的情况,因为我们需要字符串的输出来对输出进行处理或用于其他目的。

必须注意的是,我们看到的金字塔输出不是打印名为p2 的变量的结果,而是使用Popen 的执行程序的输出。

这个问题可以用多种方法来解决,下面将介绍其中的一些方法。

方法1:使用check_output ,在Python中捕获subprocess 的输出

考虑一下下面的代码:

from subprocess import check_output
out = check_output(["program\pyramid.exe"])
print(out)

输出:

b'        1 rn      2 3 2 rn    3 4 5 4 3 rn  4 5 6 7 6 5 4 rn5 6 7 8 9 8 7 6 5 rn'

输出是字节形式的,使用decode() 函数可以很容易地转换为字符串。

print(out.decode('utf-8'))

输出:

Python子进程捕获输出

这就是我们在名为out 的字符串变量中所期望的输出。

check_output() 函数运行我们想要的命令,并以字节字符串的形式返回所执行程序的命令输出。

然后我们可以使用decode() 方法将该字节字符串转换为普通字符串。decode() 参数的编码参数可以根据输出类型的不同而不同。

在我们的案例中,它是utf-8 ,但在不同的程序和不同的操作系统中,它可以有所不同。

方法2:使用Popen.communicate ,在Python中捕获subprocess 的输出。

考虑一下下面的代码:

import subprocess
pipe = subprocess.Popen("\program\pyramid.exe", stdout=subprocess.PIPE)
text = pipe.communicate()[0]
print(text)

输出:

b'        1 rn      2 3 2 rn    3 4 5 4 3 rn  4 5 6 7 6 5 4 rn5 6 7 8 9 8 7 6 5 rn'

像以前一样,返回一个字节字符串,可以很容易地使用decode() 函数返回。

print(text.decode('utf-8'))

输出:

Python子进程捕获输出

不使用check_output ,我们也可以使用Popen.communicate() 方法,check_output 的主要优点之一是它允许人们将stdoutstderr 重定向到我们想要的变量。

它的工作原理是:Popen 返回一个stdoutstderr 的元组,所以当你访问 [0]stdout你也可以做text, err = pipe.communicate() ,然后text ,就可以得到需要的东西。

就像check_outputPopen.communicate() 也会返回一个字节字符串,在把兼容和合适的编码方案传给decode 方法后,可以很容易地用decode 方法转换为普通字符串。