python subprocess模块
python模块系列之 - subprocess
subprocess – 创建附加进程
subprocess模块提供了一种一致的方法来创建和处理附加进程,与标准库中的其它模块相比,提供了一个更高级的接口。用于替换如下模块:
os.system() , os.spawnv() , os和popen2模块中的popen()函数,以及 commands().
1、subprocess.call()
subprocess的call方法可以用于执行一个外部命令,但该方法不能返回执行的结果,只能返回执行的状态码: 成功(0) 或 错误(非0)
import subprocess ret = subprocess.call(["", "-h"]) print(ret) ret = subprocess.call("df -h", shell=true) print(ret)
#!/usr/bin/python #! -*- coding:utf-8 -*- import subprocess ret = subprocess.call(["df", "-h"]) print(ret) ret = subprocess.call("df -h", shell=True) print(ret)
如上实例所示,虽然我们能看到执行的结果,但实际获取的值只是状态码
2、subprocess.check_call() 方法
check_call与call命令相同,区别是如果出错会报错
我们说过call执行返回一个状态码,我们可以通过check_call()函数来检测命令的执行结果,如果出现错误,进行报错【如果returncode不为0,则举出错误subprocess.CalledProcessError,该对象包含有returncode属性,可用try…except…来检查
import subprocess ret = subprocess.check_call(["dd", "-h"]) # dd -h ,命令不存在 print(ret)
Traceback (most recent call last): File "1.py", line 5, in <module> ret = subprocess.check_call(["dd", "-h"]) File "/usr/local/python3/lib/python3.5/subprocess.py", line 271, in check_call raise CalledProcessError(retcode, cmd) subprocess.CalledProcessError: Command '['dd', '-h']' returned non-zero exit status 1
使用try...excepe...来捕获异常
import subprocess try: ret = subprocess.check_call("etstat -tn", shell = True) except subprocess.CalledProcessError: print("error") else: print(ret)
[root@ming python]# python 2.py /bin/sh: etstat: command not found error
3、subprocess.check_output()方法
父进程等待子进程执行命令,返回子进程向标准输出发送输出运行结果,检查退出信息,如果returncode不为0,则举出错误subprocess.CalledProcessError,该对象包含有returncode属性和output属性,output属性为标准输出的输出结果,可用try…except…来检查。
import subprocess try: ret = subprocess.check_output("netstat -tn", shell = True) #可以执行成功 except subprocess.CalledProcessError: print("error") else: print(ret.decode('GBK')) # 输出执行结果
活动连接 协议 本地地址 外部地址 状态 卸载状态 TCP 127.0.0.1:443 127.0.0.1:54149 ESTABLISHED InHost TCP 127.0.0.1:443 127.0.0.1:57952 ESTABLISHED InHost TCP 127.0.0.1:8307 127.0.0.1:54150 ESTABLISHED InHost TCP 127.0.0.1:8307 127.0.0.1:57957 ESTABLISHED InHost TCP 127.0.0.1:54149 127.0.0.1:443 ESTABLISHED InHost TCP 127.0.0.1:54150 127.0.0.1:8307 ESTABLISHED InHost
import subprocess try: ret = subprocess.check_output("nestat -tn", shell = True) #命令错误 except subprocess.CalledProcessError: print("error") else: print(ret.decode('GBK')) # 输出执行结果
'nestat' 不是内部或外部命令,也不是可运行的程序 或批处理文件。 error
4、subprocess.Popen()方法
实际上,subprocess模块中只定义了一个类: Popen。上面的几个函数都是基于Popen()的封装(wrapper)。从Python2.4开始使用Popen来创建进程,用于连接到子进程的标准输入/输出/错误中去,还可以得到子进程的返回值。这些封装的目的在于让我们容易使用子进程。当我们想要更个性化我们的需求的时候,就要转向Popen类,该类生成的对象用来代表子进程。
构造函数如下:
subprocess.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)
与上面的封装不同,Popen对象创建后,主程序不会自动等待子进程完成。我们必须调用对象的wait()方法,父进程才会等待 (也就是阻塞block)。
a、父进程不等待子进程
import subprocess child = subprocess.Popen('ping www.baidu.com', shell= True) print("hello")
[root@ming python]# python 3.py hello PING www.a.shifen.com (220.181.112.244) 56(84) bytes of data. 64 bytes from 220.181.112.244: icmp_seq=1 ttl=57 time=1.93 ms 64 bytes from 220.181.112.244: icmp_seq=2 ttl=57 time=3.28 ms 64 bytes from 220.181.112.244: icmp_seq=3 ttl=57 time=3.62 ms 64 bytes from 220.181.112.244: icmp_seq=4 ttl=57 time=1.45 ms 64 bytes from 220.181.112.244: icmp_seq=5 ttl=57 time=1.46 ms
可以看出,Python并没有等到child子进程执行的Popen操作完成就执行了print操作。
b、添加子进程等待
import subprocess child = subprocess.Popen('ping -c 4 www.baidu.com', shell= True) child.wait() #子进程等待
print("hello")
[root@ming python]# python 3.py PING www.a.shifen.com (220.181.111.188) 56(84) bytes of data. 64 bytes from 220.181.111.188: icmp_seq=1 ttl=59 time=2.01 ms 64 bytes from 220.181.111.188: icmp_seq=2 ttl=59 time=2.28 ms 64 bytes from 220.181.111.188: icmp_seq=3 ttl=59 time=8.70 ms 64 bytes from 220.181.111.188: icmp_seq=4 ttl=59 time=1.82 ms --- www.a.shifen.com ping statistics --- 4 packets transmitted, 4 received, 0% packet loss, time 3041ms rtt min/avg/max/mdev = 1.824/3.707/8.701/2.888 ms hello
可以看出,print语句是等待child子进程执行完后才执行的
此外,你还可以在父进程中对子进程进行其它操作,比如我们上面例子中的child对象:
child.poll() # 检查子进程状态 child.kill() # 终止子进程 child.send_signal() # 向子进程发送信号 child.terminate() # 终止子进程 ps: 子进程的PID存储在child.pid
c、子进程文本流控制
子进程的标准输入、标准输出和标准错误如下属性分别表示:
child.stdin | child.stdout | child.stderr
我们还可以在Popen()建立子进程的时候改变标准输入、标准输出和标准错误,并可以利用subprocess.PIPE将多个子进程的输入和输出连接在一起,构成管道(pipe):
例=1
#!/usr/bin/env python import subprocess child = subprocess.Popen(['ls','-l'],stdout=subprocess.PIPE)#将标准输出定向输出到subprocess.PIPE print(child.stdout.read().decode("UTF-8")) #使用 child.communicate() 输出的是一个元组
运行结果
[root@ming python]# python 5.py total 20 -rw-r--r--. 1 root root 176 Dec 5 00:50 1.py -rw-r--r--. 1 root root 210 Dec 5 01:03 2.py -rw-r--r--. 1 root root 108 Dec 5 01:47 3.py -rw-r--r--. 1 root root 178 Dec 6 20:39 4.py -rw-r--r--. 1 root root 194 Dec 6 21:52 5.py
例=2
#!/usr/bin/env python import subprocess child1 = subprocess.Popen(['cat','/etc/passwd'],stdout=subprocess.PIPE) child2 = subprocess.Popen(['grep','root'],stdin=child1.stdout,stdout=subprocess.PIPE) a = child2.communicate() #是一个元组 for i in a: if i: print(i.decode("UTF-8"))
运行结果 [root@ming python]# python 6.py root:x:0:0:root:/root:/bin/bash operator:x:11:0:operator:/root:/sbin/nologin
subprocess.PIPE实际上为文本流提供一个缓存区。child1的stdout将文本输出到缓存区,随后child2的stdin从该PIPE中将文本读取走。child2的输出文本也被存放在PIPE中,直到communicate()方法从PIPE中读取出PIPE中的文本。
注意:communicate()是Popen对象的一个方法,该方法会阻塞父进程,直到子进程完成