添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

发布于:2018-01-12 | 分类: python/vba/cpp

从python2.4开始,内置的 subprocess 模块可以创建子进程并连接子进程的标准输入/输出/错误,因此可以用来执行外部程序并获取执行结果和输出。本文示例基于Python2.7,转为Python3代码时需要考虑对 bytes 类型返回值的 decode() 转码。

Popen类方法

subprocess 模块通过 Popen 类完成创建子进程并与其交互的功能,常见的几个成员函数如下:

  • Popen.poll() 检查子进程是否已经结束,未结束返回 None ,结束返回 returncode 属性值
  • Popen.wait() Popen.communicate() 都会阻塞父进程,直到子进程结束
  • Popen.communicate(input=None) 与子进程交互:向 stdin 发送数据,从 stdout stderr 读取数据
  • 创建 Popen 对象后,主程序不会自动等待子进程完成

    以上三个成员函数都可以用于等待子进程返回: while 循环配合 Popen.poll() Popen.wait() Popen.communicate() 。由于后面二者都会阻塞父进程,所以无法 实时 获取子进程输出,而是等待子进程结束后一并输出所有打印信息。另外, Popen.wait() Popen.communicate() 分别将输出存放于管道和内存,前者容易超出默认大小而导致死锁,因此不推荐使用。

    Popen类属性

    Popen 类具有三个与输入输出相关的属性: Popen.stdin , Popen.stdout Popen.stderr ,分别对应子进程的标准输入/输出/错误。Python的 sys 模块定义了标准输入/输出/错误:

    sys.stdin  # 标准输入
    sys.stdout # 标准输出
    sys.stderr # 标准错误信息    

    以上三个对象类似于文件流,因此可以使用 readline() write() 方法进行读写操作。例如打印标准输出的 print 指令等效于 sys.stdout.write()

    需要注意的是,除了直接向控制台打印输出外,标准输出/错误的打印存在缓存,为了实时输出打印信息,需要执行

    sys.stdout.flush()
    sys.stderr.flush()

    标准输入/输出/错误参数

    Popen 类构造函数中的三个参数 stdin , stdout stderr 用以指定执行程序的标准输入/输出/错误的文件句柄。它们的值可以是 PIPE 、文件描述符(正整数)、文件对象或 None

  • PIPE 表示创建一个连接子进程的新管道,默认值 None , 表示不做重定向。
  • 子进程的文件句柄可以从父进程中继承得到。
  • stderr 可以设置为 STDOUT ,表示将子进程的标准错误重定向到标准输出。
  • 参考示例:

     import subprocess
     child1 = subprocess.Popen(["ls","-l"], stdout=subprocess.PIPE)
     child2 = subprocess.Popen(["wc"], stdin=child1.stdout, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
     out = child2.communicate()

    其中, subprocess.PIPE 为文本流提供一个缓存区,child1的 stdout 将文本输出到缓存区;随后child2的 stdin 从该 PIPE 读取文本,child2的输出文本也被存放在 PIPE 中,而标准错误信息则重定向到标准输出;最后, communicate() 方法从 PIPE 中读取child2子进程的标准输出和标准错误。

    假设被调用的Python代码如下,其中的 flush() 方法作用为及时清空缓存,以便主程序实时获取其输出信息。

    # coding:utf-8
    # file: called.py
    import time, sys
        # 获取标准输入
        N = int(sys.stdin.readline())   
    except ValueError:
        # 标准错误信息
        sys.stdout.write("numeric input required!!\n")
        sys.stdout.flush()
        exit(1)
    else:
        for i in range(N):
            # 标准输出
            sys.stdout.write("%d\n" %i)     
            sys.stdout.flush()
            time.sleep(1)
        exit(0)

    主程序 call.py

    import subprocess, shlex
    command = "python called.py"
    p = subprocess.Popen(shlex.split(command), stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    # 为子进程传递参数
    p.stdin.write('5\n') 
    # 实时获取输出
    while p.poll() == None:
        out = p.stdout.readline().strip()
        if out:
            print "sub process output: ", out
    # 子进程返回值
    print "return code: ", p.returncode
  • stdin=subprocess.PIPE 指定子程序输入方式,接着由 p.stdin.write('5\n') 给定;子程序中使用 sys.stdin.readline() 获取
  • stderr=subprocess.STDOUT 将标准错误重定向到标准输出,于是可以使用 p.stdout.readline() 统一获取标准输出和标准错误信息
  • 参考资料

  • 17.1. subprocess — Subprocess management
  • python子进程模块subprocess详解
  • python子进程模块subprocess详解与应用实例 之三
  • Python中print如何刷新缓冲立刻打印输出结果
  •