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对象的一个方法,该方法会阻塞父进程,直到子进程完成

原文地址:https://www.cnblogs.com/ming5218/p/7976453.html