subprocess模块
run函数
Run函数是在python3.5之后新增,语法如下
subprocess.run(args, *, stdin=None, input=None, stdout=None, stderr=None, shell=False, cwd=None, timeout=None, check=False, encoding=None, errors=None)
(
具体解释参考 https://docs.python.org/3/library/subprocess.html#module-subprocess)
上面的参数仅仅是基本的常用功能,
我个人推荐使用run函数来使用基本功能,代替原有的call等函数
当更复杂的情况下,可以使用底层的popen接口
1.call()方法:
父进程等待子进程完成
返回退出信息(returncode,相当于Linux exit code)
Subprocess.call()=subprocess.run().returncode
>>> subprocess.run(
'ping -c1 -W1 127.0.0.1>/dev/null'
,
shell=
True).returncode
0
>>> subprocess.call(
'ping -c1 -W1 127.0.0.1>/dev/null'
,
shell=
True)
0
上面两行的代码内容是一样的,(注意shell=True的T一定是大写的)
所以call是旧版的高级api,subprocess的call方法可以用于执行一个外部命令,但该方法不能返回执行的结果,只能返回执行的状态码: 成功(0) 或 错误(非0)
>>> a=subprocess.call(
'ping -c1 127.0.0.1'
,
shell=
True)
PING
127.0.0.1 (
127.0.0.1):
56 data
bytes
64
bytes
from
127.0.0.1: icmp_seq=
0 ttl=
64 time=
0.054 ms
---
127.0.0.1 ping statistics ---
1 packets transmitted
,
1 packets received
,
0.0% packet loss
round-trip min/avg/max/stddev =
0.054/
0.054/
0.054/
0.000 ms
>>>
print(a)
0
可以看到call()目的是返回值,可以用于判断命令是否成功。
2.check_call()方法
父进程等待子进程完成
返回0
检查退出信息,如果returncode不为0,则举出错误subprocess.CalledProcessError,该对象包含有returncode属性,可用try…except…来检查
subprocess.check_call()=subprocess.run(…,check=True)
try:
subprocess.check_call(
"ls /test",
shell=True)
except subprocess.CalledProcessError as error:
print(
"check_all test:",error)
结果如下
/anaconda3/bin/python3 /Users/wangyizhe/Desktop/pythonIDE/python3/test.py
check_all test
ls: /test: No such file or directory
Process finished with exit code 0
可以看到check_call()目的是可以对错误进行处理,一般配合try…expect使用。
同理上面一段代码可以用run()方法代替
try:
subprocess.run(
"ls /test"
,
shell=
True,
check=
True)
except subprocess.CalledProcessError
as error:
print(
"check_all test:"
,error)
3.check_output() 方法
call()方法启动的进程,其标准输入输出会绑定到父进程的输入和输出。调用程序无法获取命令的输出结果。但可以通过check_output()方法来捕获输出。
subprocess.check_output()=run(..., check=True, stdout=PIPE).stdout
a=subprocess.check_output(
'ls -l'
,
shell=
True,
stderr=subprocess.STDOUT)
print(a.decode(‘utf-8'))
结果如下
/anaconda3/bin/python3 /Users/wangyizhe/Desktop/pythonIDE/python3/test.py
b'crw-rw-rw- 1 root wheel 3, 2 Feb 20 10:51 /dev/null\n'
Process finished with exit code 0
同理,可以用run函数代替
a=subprocess.run(
'ls -l /dev/null'
,
shell=
True,
stdout=subprocess.PIPE).stdout
print(a)
所以可见check_output可以打印输出结果
如果不定位stdout
a=subprocess.run(
'ls -l /dev/null'
,
shell=
True,
stdout=subprocess.PIPE)
print(a)
结果如下
/anaconda3/bin/python3 /Users/wangyizhe/Desktop/pythonIDE/python3/test.py
CompletedProcess(args='ls -l /dev/null', returncode=0, stdout=b'crw-rw-rw- 1 root wheel 3, 2 Feb 20 10:54 /dev/null\n')
Process finished with exit code 0
注意上面紫色字体,completedProcess就是run()函数的所有输出结果,包含args、returncode、stdout等
4.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方法是一种直接处理管道与子进程通信的方法。
参数args可以是字符串或者序列类型(如:list,元组),用于指定进程的可执行文件及其参数。如果是序列类型,第一个元素通常是可执行文件的路径。我们也可以显式的使用executeable参数来指定可执行文件的路径。
参数stdin, stdout, stderr分别表示程序的标准输入、输出、错误句柄。他们可以是PIPE,文件描述符或文件对象,也可以设置为None,表示从父进程继承。
如果参数shell设为true,程序将通过shell来执行。
参数env是字典类型,用于指定子进程的环境变量。如果env = None,子进程的环境变量将从父进程中继承。
下面是popen的各种方法
Popen.poll():用于检查子进程是否已经结束。设置并返回returncode属性。
Popen.wait():等待子进程结束。设置并返回returncode属性。
Popen.communicate(input=None):与子进程进行交互。向stdin发送数据,或从stdout和stderr中读取数据。可选参数input指定发送到子进程的参数。Communicate()返回一个元组:(stdoutdata, stderrdata)。注意:如果希望通过进程的stdin向其发送数据,在创建Popen对象的时候,参数stdin必须被设置为PIPE。同样,如果希望从stdout和stderr获取数据,必须将stdout和stderr设置为PIPE。
Popen.send_signal(signal):向子进程发送信号。
Popen.terminate():停止(stop)子进程。在windows平台下,该方法将调用Windows API TerminateProcess()来结束子进程。
Popen.kill():杀死子进程。
Popen.pid:获取子进程的进程ID。
Popen.returncode:获取进程的返回值。如果进程还没有结束,返回None。
实际上,上面的几个函数(call()、check_call()等都是基于Popen()的封装(wrapper)。这些封装的目的在于让我们容易使用子进程。当我们想要更个性化我们的需求的时候,就要转向Popen类,该类生成的对象用来代表子进程。
与上面的封装不同,Popen对象创建后,主程序不会自动等待子进程完成。我们必须调用对象的wait()方法,父进程才会等待 (也就是阻塞block)
我们来看个例子
例子1:
import subprocess
child=subprocess.Popen(
'ls -l'
,
shell=
True)
parent=
print(
'parent process
’)
结果
/anaconda3/bin/python3 /Users/wangyizhe/Desktop/pythonIDE/python3/test.py
parent process
total 24
drwxr-xr-x 3 wangyizhe staff 96 Feb 19 11:23 __pycache__
-rw-r--r-- 1 wangyizhe staff 104 Feb 20 11:16 test.py
-rw-r--r-- 1 wangyizhe staff 548 Feb 18 20:12 test_fun_time.py
-rw-r--r-- 1 wangyizhe staff 628 Feb 19 23:35 testa.py
drwxr-xr-x 7 wangyizhe staff 224 Feb 19 11:16 venv
drwxr-xr-x 6 wangyizhe staff 192 Feb 19 17:19 venv1
Process finished with exit code 0
可以看到在子进程没有结束,代码先执行了下面的parenet父进程,然后在执行子进程,
所以子进程是不会自动完成的。
在对比一下
import subprocess
child=subprocess.Popen(
'ls -l'
,
shell=
True)
child.wait()
parent=
print(
'parent process
’)
增加了child.wait()
就会先直执行完子进程在执行父进程,结果如下
/anaconda3/bin/python3 /Users/wangyizhe/Desktop/pythonIDE/python3/test.py
total 24
drwxr-xr-x 3 wangyizhe staff 96 Feb 19 11:23 __pycache__
-rw-r--r-- 1 wangyizhe staff 104 Feb 20 11:19 test.py
-rw-r--r-- 1 wangyizhe staff 548 Feb 18 20:12 test_fun_time.py
-rw-r--r-- 1 wangyizhe staff 628 Feb 19 23:35 testa.py
drwxr-xr-x 7 wangyizhe staff 224 Feb 19 11:16 venv
drwxr-xr-x 6 wangyizhe staff 192 Feb 19 17:19 venv1
parent process
Process finished with exit code 0
例子2:
#直接打印输出结果
>>> subprocess.Popen('ls -l',shell=True)
>>> total 0
drwxr-xr-x 5 wangyizhe staff 160 Feb 18 10:34 python2
drwxr-xr-x 9 wangyizhe staff 288 Feb 20 11:37 python3
#将结果输入到变量中
>>> a=subprocess.Popen('ls -l',shell=True,stdout=subprocess.PIPE)
>>> result=
a.communicate()
>>> result
(b'total 0\ndrwxr-xr-x 5 wangyizhe staff 160 Feb 18 10:34 python2\ndrwxr-xr-x 9 wangyizhe staff 288 Feb 20 11:37 python3\n', None)
#打印变量的结果,注意是个元组
>>> print(result.decode('utf-8'))
Traceback (most recent call last):
File "", line 1, in
AttributeError: 'tuple' object has no attribute 'decode'
>>> print(result[0].decode('utf-8'))
total 0
drwxr-xr-x 5 wangyizhe staff 160 Feb 18 10:34 python2
drwxr-xr-x 9 wangyizhe staff 288 Feb 20 11:37 python3
#将结果打印到文件中
>>> file=open('/Users/wangyizhe/Desktop/file.txt','w+')
>>>
subprocess.Popen("ls -l",shell=True,stdout=file)
>>>
在相应目录下就保存了文件file
注意:communicate()是Popen对象的一个方法,该方法会阻塞父进程,直到子进程完成
例子3:
实在在终端输入python3,然后打印’wyztest’
>>>
import subprocess
>>> proc=subprocess.Popen([
'python3']
,
stdin=subprocess.PIPE
,
stdout=subprocess.PIPE
,
stderr=subprocess.PIPE)
>>> proc.stdin.write(
'print("wyztest")')
Traceback (most recent call last):
File
""
, line
1
, in
TypeError: a
bytes-like
object
is required
, not
'str'
>>> proc.stdin.write(
'print("wyztest")'.encode(
'utf-8'))
16
>>> output_val
,err_val= proc.communicate()
>>> output_val
b'wyztest
\n
'
>>> err_val
b''
>>>
第一步输入python3进入交互式环境,然后通过stdin.write写入print wyztest(注意这里的编码问题,需要编码成utf-8),然后通过communicate结束子进程并复制stdout和stderr给output_val和err_val