【转】 Python subprocess模块学习总结

时间:2023-03-08 20:29:46
【转】 Python subprocess模块学习总结
从Python 2.4开始,Python引入subprocess模块来管理子进程,以取代一些旧模块的方法:如 os.system、os.spawn*、os.popen*、popen2.*、commands.*不但可以调用外部的命令作为子进程,而且可以连接到子进程的input/output/error管道,获取相关的返回信息

一、subprocess以及常用的封装函数
运行python的时候,我们都是在创建并运行一个进程。像Linux进程那样,一个进程可以fork一个子进程,并让这个子进程exec另外一个程序。在Python中,我们通过标准库中的subprocess包来fork一个子进程,并运行一个外部的程序。
subprocess包中定义有数个创建子进程的函数,这些函数分别以不同的方式创建子进程,所以我们可以根据需要来从中选取一个使用。另外subprocess还提供了一些管理标准流(standard stream)和管道(pipe)的工具,从而在进程间使用文本通信。

subprocess.call()
父进程等待子进程完成
返回退出信息(returncode,相当于Linux exit code)

subprocess.check_call()
父进程等待子进程完成
返回0
检查退出信息,如果returncode不为0,则举出错误subprocess.CalledProcessError,该对象包含有returncode属性,可用try…except…来检查

subprocess.check_output()
父进程等待子进程完成
返回子进程向标准输出的输出结果
检查退出信息,如果returncode不为0,则举出错误subprocess.CalledProcessError,该对象包含有returncode属性和output属性,output属性为标准输出的输出结果,可用try…except…来检查。

这三个函数的使用方法相类似,下面来以subprocess.call()举例说明:

import subprocess
retcode = subprocess.call('ping www.baidu.com')
print retcode

subprocess.Popen()

class Popen(args, bufsize=0, executable=None, stdin=None, stdout=None, stderr=None, preexec_fn=None, close_fds=False, shell=False, cwd=None, env=None, universal_newlines=False, startupinfo=None, creationflags=0)
args 字符串或者列表
bufsize 0 无缓冲
1 行缓冲
其他正值 缓冲区大小
负值 采用默认系统缓冲(一般是全缓冲)
executable 一般不用,args字符串或列表第一项表示程序名
stdin
stdout
stderr
None 没有任何重定向,继承父进程
PIPE 创建管道
文件对象
文件描述符(整数)
stderr 还可以设置为 STDOUT
preexec_fn 钩子函数, 在fork和exec之间执行。(unix)
close_fds unix 下执行新进程前是否关闭0/1/2之外的文件
windows下不继承还是继承父进程的文件描述符
shell 为真的话
unix下相当于args前面添加了 "/bin/sh“ ”-c”
window下,相当于添加"cmd.exe /c"
cwd 设置工作目录
env 设置环境变量
universal_newlines 各种换行符统一处理成 '\n'
startupinfo window下传递给CreateProcess的结构体
creationflags

windows下,传递CREATE_NEW_CONSOLE创建自己的控制台窗口

creationflags=0x08000000        #测试过程中不显示shell window

其中的args 参数,可以是一个字符串,可以是一个列表。

  • UNIX下
subprocess.Popen(["gedit","abc.txt"])
subprocess.Popen("gedit abc.txt")

这两个之中,后者将不会工作。因为如果是一个字符串的话,必须是程序的路径才可以。(考虑unix的api函数 exec,接受的是字符串列表)

但是下面的可以工作

subprocess.Popen("gedit abc.txt", shell=True)

这是因为它相当于  

subprocess.Popen(["/bin/sh", "-c", "gedit abc.txt"])
  • Windows下
subprocess.Popen(["notepad.exe", "abc.txt"])
subprocess.Popen("notepad.exe abc.txt")

这是由于windows下的api函数CreateProcess接受的是一个字符串。即使是列表形式的参数,也需要先合并成字符串再传递给api函数。  

类似上面

subprocess.Popen("notepad.exe abc.txt",shell=True)

等价于  

subprocess.Popen("cmd.exe /C "+"notepad.exe abc.txt",shell=True)

实际上,上面的几个函数都是基于Popen()的封装(wrapper)。这些封装的目的在于让我们容易使用子进程。当我们想要更个性化我们的需求的时候,就要转向Popen类,该类生成的对象用来代表子进程。

与上面的封装不同,Popen对象创建后,主程序不会自动等待子进程完成。我们必须调用对象的wait()方法,父进程才会等待 (也就是阻塞block),举例

import subprocess
child = subprocess.Popen('ping www.baidu.com')
child.wait()
print 'parent process'

从运行结果中看到,父进程在开启子进程之后并等待child的完成后,再运行print。
此外,你还可以在父进程中对子进程进行其它操作,比如我们上面例子中的child对象:

child.poll() # 检查子进程状态 结果PASS,返回0;Fail,返回1;运行中时无值
child.kill() # 终止子进程
child.send_signal() # 向子进程发送信号
child.terminate() # 终止子进程
child.pid() #子进程PID

  

二、子进程的文本流控制

子进程的标准输入、标准输出和标准错误如下属性分别表示:

child.stdin
child.stdout
child.stderr

可以在Popen()建立子进程的时候改变标准输入、标准输出和标准错误,并可以利用subprocess.PIPE将多个子进程的输入和输出连接在一起,构成管道(pipe),如下2个例子:

import subprocess
child1 = subprocess.Popen(’ping www.baidu.com‘, stdout=subprocess.PIPE)
print child1.stdout.read()
#或者child1.communicate()

结果如下:

正在 Ping www.a.shifen.com [180.97.33.108] 具有 32 字节的数据:

来自 180.97.33.108 的回复: 字节=32 时间=8ms TTL=54

来自 180.97.33.108 的回复: 字节=32 时间=7ms TTL=54

来自 180.97.33.108 的回复: 字节=32 时间=9ms TTL=54

来自 180.97.33.108 的回复: 字节=32 时间=7ms TTL=54

180.97.33.108 的 Ping 统计信息:

    数据包: 已发送 = 4,已接收 = 4,丢失 = 0 (0% 丢失),

往返行程的估计时间(以毫秒为单位):

    最短 = 7ms,最长 = 9ms,平均 = 7ms