Date | Tags |
---|---|
2019/10/8 | Python3, subprocess |
subprocess
,译子进程。
在运行Python程序时,都是在创建并运行一个进程
。在Python中,可通过标准库的subprocess模块来fork一个子进程,并运行一个外部程序(如执行一条cmd命令)。subprocess还提供了管理标准流(standard stream)和管道(pipe)的工具,从而在进程之间使用文本通信。
subprocess模块是Python 2.4
新增的一个模块,用来替换几个旧的模块和方法
(如os.system()、os.spawn*(),甚至os.popen,当然commands更是舍弃了)。
subprocess.run()函数
subprocess.run(args, *, stdin=None, input=None, stdout=None, stderr=None, capture_output=False, shell=False, cwd=None, timeout=None, check=False, encoding=None, errors=None, text=None, env=None, universal_newlines=None)
Python 3.5
中新增的函数。执行指定的命令,等待命令执行完成后返回一个包含执行结果的CompleteProcess
类的实例。Python官方建议
是尽量使用subprocess.run()函数。
1.1 特有的一些参数:
参数 | 说明 |
---|---|
capture_output |
设为 true,stdout 和 stderr 将会被捕获 。相当于设置stdout=PIPE 和 stderr=PIPE, 所以这两者不要同时设置。 |
timeout |
参数将被传递给 Popen.communicate()。如果发生超时 ,子进程将被杀死并等待。 TimeoutExpired 异常 将在子进程中断后被抛出。 |
input |
该参数是传递给Popen.communicate() ,通常该参数的值必须是一个字节序列,如果encoding 或 errors 或者将 text 设置为 True ,则其值应该是一个字符串。 |
check |
如果 check 设为 True, 并且进程以非零状态码 退出, 则会抛出一个CalledProcessError 的异常。且该异常对象会包含参数、退出状态码以及标准输出和标准错误, 如果被捕获到。 |
1.2 常见一些参数:
参数 | 说明 |
---|---|
args |
要执行的shell命令,默认是一个字符串序列,如['ls', '-al']或('ls', '-al') ;也可是一个字符串,如'ls -al' ,同时需要设置shell=True 。 |
shell |
如果shell为True,那么指定的命令将通过shell执行。 |
cwd |
函数在执行子进程前改变当前工作目录 为 cwd。 |
check |
如果check参数的值是True,且执行命令的进程以非0状态码退出,则会抛出一个CalledProcessError的异常,且该异常对象会包含参数、退出状态码、以及stdout和stderr(如果它们有被捕获的话)。 |
stdout,stderr |
run()函数 默认不会捕获命令执行结果的正常输出和错误输出,可以设置stdout=PIPE, stderr=PIPE 来捕获相应的内容;也可以设置stderr=STDOUT ,使标准错误通过标准输出流输出 。 |
1.3 举例
import subprocess
cmd = 'pwd'
result = subprocess.run(cmd, shell=True, timeout=3, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
print (result)
CompletedProcess(args='pwd', returncode=0, stdout=b'/home/ta\n', stderr=b'')
print (result.returncode)
0
print (result.stdout)
b'/home/ta\n'
try:
result = subprocess.run('d', shell=True, timeout=3, stderr=subprocess.PIPE, stdout=subprocess.PIPE, check=True)
except Exception as e:
print (e.args)
print (e.cmd)
print (e.stderr)
print (e.stdout)
print (e.returncode)
(127, 'd')
d
b'/bin/sh: d: command not found\n'
b''
127
result = subprocess.run('d', shell=True, timeout=3, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
print (result)
CompletedProcess(args='d', returncode=127, stdout=b'', stderr=b'/bin/sh: d: command not found\n')
subprocess.popen类:
subprocess.Popen类用于创建一个新的进程,让其执行一个子程序,并与它进行通信,获取标准输入、输出、错误以及返回码
。
subprocess中的run()、call()、check_call()、check_output()
这些函数均是基于subprocess.Popen
类实现的,所以当现有函数都无法满足需求时,可使用subprocess.Popen
类来封装实现。
2.1 subprocess.Popen类 的构造函数:
class subprocess.Popen(args, bufsize=-1, executable=None, stdin=None, stdout=None, stderr=None, preexec_fn=None, close_fds=True, shell=False, cwd=None, env=None, universal_newlines=False, startupinfo=None, creationflags=0, restore_signals=True, start_new_session=False, pass_fds=(), *, encoding=None, errors=None)
Popen类的对象创建之后,主程序不会自动等待子进程完成
。所以必须调用wait()
方法,父进程才会等待(即阻塞 block)。
参数说明:
-
args
: 要执行的shell命令
,可以是字符串,也可以是序列。 -
bufsize
: 指定缓存策略
,0表示不缓冲,1表示行缓冲,其他大于1的正值数字表示缓冲区大小,负数表示使用系统默认缓冲策略。 -
stdin, stdout, stderr
: 分别表示程序标准输入、输出、错误
句柄。 -
preexec_fn
: 用于指定一个将在子进程运行之前被调用的可执行对象,只在Unix平台下有效。 -
close_fds
: 如果该参数的值为True,则除了0,1和2之外的所有文件描述符
都将会在子进程执行之前被关闭。 -
env
: 用于指定子进程的环境变量
,如果env=None,那么子进程的环境变量将从父进程中继承。如果env!=None,它的值必须是一个映射对象。 -
universal_newlines
: 如果该参数值为True,则该文件对象的stdin,stdout和stderr将会作为文本流
被打开,否则他们将会被作为二进制流
被打开。
2.2 subprocess.Popen类的实例(对象)可调用的方法:
方法名 | 说明 |
---|---|
Popen.poll()
|
用于检查子进程 (命令)是否已经执行结束 ,没结束返回None,结束后返回状态码。 |
Popen.wait(timeout=None)
|
等待子进程结束 ,并返回状态码;如果在timeout指定的秒数之后进程还没有结束,将会抛出一个TimeoutExpired异常。 |
Popen.communicate(input=None, timeout=None)
|
该方法可用来与进程进行交互 ,比如发送数据到stdin,从stdout和stderr读取数据,直到到达文件末尾。 |
Popen.send_signal(signal)
|
发送 指定的信号 给这个子进程。 |
Popen.terminate()
|
停止该子进程 。 |
Popen.kill()
|
杀死该子进程 。 |
communicate()方法详解
:该方法会阻塞父进程,直到子进程完成。
- 该方法中的可选参数
input
应该是将被发送给子进程的数据,默认input=None表示没有数据发送给子进程。input参数的数据类型必须是字节序列或者字符串。 - 该方法
返回一个元组
(stdout_data, stderr_data),这些数据将会是字节或字符串。 - timeout秒数后
超时
,将会抛出一个TimeoutExpired异常。捕获这个异常,然后重新通信
不会丢失任何输出的数据。但是超时之后子进程并没有被杀死,所以手动杀死这个子进程来结束通信。 - 注意读取的
数据是缓冲在内存中
的,所以数据非常大或者是无限大时不建议使用这个方法。
2.3 subprocess.Popen类的实例(对象)拥有的属性:
属性名 | 说明 |
---|---|
Popen.pid
|
获取子进程的进程ID
|
Popen.returncode
|
获取进程的返回码 。如果进程未结束,则返回None |
Popen.args
|
获取传递给Popen的args参数。它是一个字符串或一个参数序列 |
Popen.stdin
|
获取stdin参数值。 |
Popen.stdout
|
获取stdout参数值。 |
Popen.stderr
|
获取stderr参数值。 |
其他变量
名称 | 说明 |
---|---|
subprocess.DEVNULL
|
是一个特殊值,可用于Popen对象 的stdin、stdout、stderr参数,表示使用特殊文件os.devnull。Python 3.3中新增的。 |
subprocess.PIPE
|
在创建Popen对象 时,subprocess.PIPE可以初始化为stdin, stdout或stderr的参数 ,表示与子进程通信的标准输入流,标准输出流以及标准错误。 |
subprocess.STDOUT
|
作为Popen对象 的stderr的参数,表示将标准错误通过标准输出流输出 。 |
subprocess.SubprocessError
|
Python 3.3新增的。它是subprocess模块中所有其他异常类的基类 。 |
subprocess.TimeoutExpired
|
Python 3.3新增的。是SuprocessError的子类 。当等待子进程超时 时引发。 |
subprocess.CalledProcessError
|
Python 3.3新增的。是SubprocessError的子类 。当使用check_call() 或check_output() 运行一个进程,返回一个非0退出状态码 时,引发该异常。 |
其他函数
函数名 | 说明 |
---|---|
subprocess.call () |
执行指定命令,返回命令执行状态。其功能类似于os.system(cmd) 。 |
subprocess.check_call () |
Python 2.5 中新增的函数。执行指定命令,如果执行成功则返回状态码 0 ,否则抛出subprocess.CalledProcessError异常 ,该对象包含returncode属性,可用try…except语句块进行检查。其功能等价于subprocess.run(..., check=True)。 |
subprocess.check_output () |
Python 2.7 中新增的函数。执行指定的命令,如果执行状态码为0 ,则返回命令执行结果 ,否则抛出subprocess.CalledProcessError异常 ,该属性包含returncode属性、output属性,ouput属性为标准输出的输出结果,可用try…except语句块进行检查。 |
subprocess.getoutput (cmd) |
接收字符串格式的命令,执行命令并返回执行结果,其功能类似于os.popen(cmd) .read()和commands.getoutput (cmd)。 |
subprocess.getstatusoutput (cmd) |
执行cmd命令,返回一个元组(命令执行状态、命令执行结果输出),其功能类似于commands.getstatusoutput() 。 |
上述函数均是父进程等待子进程完成,然后返回对应的结果。
- 在
Python 3.5之后
的版本,在使用subprocess模块的功能时,官方文档提倡使用subprocess.run()
函数 来替代其他函数;这也就是官方文档中提到的Older high-level API。 - 在
Python 3.5之前
的版本,在使用subprocess模块的功能时,可使用上述如subprocess.call()、subprocess.getoutput()
等函数。 -
subprocess.run()、subprocess.call()、subprocess.check_call()、subprocess.check_output()
都是通过对subprocess.Popen类的封装来实现的高级函数。因此,若要实现更复杂的功能,可通过subprocess.Popen类来完成。 -
subprocess.getoutput()、subprocess.getstatusoutput()函数
均是来自Python 2.7的commands模块的两个遗留函数
。它们隐式地调用系统shell,并且不保证其他函数所具有的安全性和异常处理的一致性。而且,两者从Python 3.3.4才开始支持Windows平台。
归纳
在编码时,需要根据Python 版本的使用不同的subprocess函数。
当然,如果以上函数都无法满足需求时,可使用subprocess.Popen
类来封装实现。
Python version | 描述 |
---|---|
Python >=2.4 | 引入了subprocess模块 用来替换os.system()、os.popen()、os.spawn*()等函数、以及Python 2.7的command模块 |
Python >=2.4 and <=3.5 | Python官方给出的建议是使用subprocess.call() 函数。Python 2.5新增了一个subprocess.check_call() 函数;Python 2.7新增了subprocess.check_output() 函数 |
Python >=3.5 | Python官方给出的建议是尽量使用subprocess.run() 函数 |
引用: