一、subprocess 模块简介
subprocess模块用来生成子进程,并可以通过管道连接它们的输入/输出/错误,以及获得它们的返回值。
它用来代替多个旧模块和函数:
os.system
os.spawn*
os.popen*
popen2.*
commands.*
subprocess.call(args, *, stdin=None, stdout=None, stderr=None, shell=False)¶
subprocess.check_call(args, *, stdin=None, stdout=None, stderr=None, shell=False)¶
subprocess.check_output(args, *, stdin=None, stderr=None, shell=False, universal_newlines=False)¶
- subprocess.PIPE
使用Popen时,用于 stdin, stdout和stderr参数的特殊值,表示打开连接标准流的管道。 - subprocess.STDOUT
使用Popen时,用于 stderr 参数的特殊值,表示将标准错误重定向到标准输出的同一个句柄。 - 异常 subprocess.CalledProcessError
当由 check_call()或 check_output()运行的进程返回非零状态值时抛出的异常。 - returncode
子进程的退出状态。 - cmd
子进程执行的命令。 - output
如果check_output()抛出异常时,子进程的输出值。
否则,没有这个值。
- 常用的参数
为了支持各种用户使用情况 ,Popen构建函数接收多种可选参数。
对于最典型的情况,许多参数都保留有安全的默认值,这些最常用的方式如下
- args
这个参数:单个字符,或者序列,传递多个字符串必须带shell=True,否则失败
-
48
-rw-r--r-- 1 root root 5383 Nov 22 19:21 all.py
-rw-r--r-- 1 root root 173 Nov 23 11:16 basics.py
-rwxr-xr-x 1 root root 4164 Nov 21 13:59 get_linux_status.py
-rw-r--r-- 1 root root 345 Nov 22 16:01 hostname.py
-rw-r--r-- 1 root root 180 Nov 22 16:06 local_ip.py
-rwxr-xr-x 1 root root 3198 Nov 22 14:58 mysql_status.py
-rw-r--r-- 1 root root 10786 Nov 23 16:05 prog.py
-rw-r--r-- 1 root root 269 Nov 23 16:43 supro.py
0subprocess.call("ls -l")
Traceback (most recent call last):File "", line 1, in
File "/usr/lib/python2.7/subprocess.py", line 522, in call
return Popen(popenargs, *kwargs).wait()
File "/usr/lib/python2.7/subprocess.py", line 710, in init
errread, errwrite)
File "/usr/lib/python2.7/subprocess.py", line 1327, in _execute_child
raise child_exception
OSError: [Errno 2] No such file or directory1. shell
如果你使用Python来作为流程控制,那这样的设置会很有用,因为它提供了绝大多数的系统shell命令且可以很方便地使用
shell的各种功能,如 shell 管道,文件名通配符,环境变量扩展,以及用户目录扩展符 ~。
但是,需要注意的是,Python 提供了类似shell功能的实现。
- stdin, stdout 和 stderr
stdin, stdout和stderr指定了执行程序的标准输入,标准输出和标准错误的文件句柄。
它们的值可以是PIPE, 一个存在的文件描述符(正整数),一个存在的文件对象,或 None.
PIPE 表示创建一个连接子进程的新管道。
默认值 为 None, 表示不做重定向。
子进程的文件句柄可以从父进程中继承得到。
另外,stderr可以设置值为 STDOUT,表示子进程的错误数据可以和标准输出是同一个文件句柄。
当stdout 或 stderr的值为管道 并且 universal_newlines的值为真时,
对于以 ‘U'模式参数打开的新行,所有行的结束都会转换成'n'。
- Popen构建函数
subprocess中更底层的进程创建和管理可以通过Popen类实现。
它提供了更多的灵活性,程序员通过它能处理更多复杂的情况。
语法:
class 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)
语义:
在新进程中执行一个子程序。
在Unix中,这个类使用 类似于 os.execvp()方式来执行子程序。
在Windows中,这个类使用Windows的 CreateProcess()函数来执行子程序。
args: 一个程序参数序列,或者单个字符串。
默认的,要执行的程序应该是序列的第一个字段。
如果单个字符串,它的解析依赖于平台
在Unix中,如果 args是一个字符串,那么这个字符串解释成被执行程序的名字或路径。
然而,这种情况只能用在不需要参数的程序。
示例代码:
- Popen对象创建后,主程序不会自动等待子进程完成。
我们必须调用对象的wait()方法,父进程才会等待 (也就是阻塞block):
import subprocess
child = subprocess.Popen(["ping","-c","5","172.16.3.33"])
print("xxxxxxxxxxxxxxxxxxxx")
#从运行结果中可看出从运行结果中看到,父进程在开启子进程之后并没有等待child的完成,而是直接运行print。
import subprocess
child = subprocess.Popen(["ping","-c","10","www.baidu.com"])
print("xxxxxxxxxxxxxxxxxxxx")
xxxxxxxxxxxxxxxxxxxx
PING www.a.shifen.com (115.239.210.27): 56 data bytes
64 bytes from 115.239.210.27: icmp_seq=0 ttl=56 time=4.420 ms
64 bytes from 115.239.210.27: icmp_seq=1 ttl=56 time=4.005 ms
64 bytes from 115.239.210.27: icmp_seq=2 ttl=56 time=52.775 ms
64 bytes from 115.239.210.27: icmp_seq=3 ttl=56 time=3.841 ms
64 bytes from 115.239.210.27: icmp_seq=4 ttl=56 time=64.287 ms
Request timeout for icmp_seq 5
64 bytes from 115.239.210.27: icmp_seq=6 ttl=56 time=3.202 ms
64 bytes from 115.239.210.27: icmp_seq=7 ttl=56 time=3.924 ms
64 bytes from 115.239.210.27: icmp_seq=8 ttl=56 time=3.425 ms
64 bytes from 115.239.210.27: icmp_seq=9 ttl=56 time=3.054 ms
--- www.a.shifen.com ping statistics ---
10 packets transmitted, 9 packets received, 10.0% packet loss
round-trip min/avg/max/stddev = 3.054/15.881/64.287/22.961 ms
- 对比等待的情况:
- subprocess
child = subprocess.Popen(["ping","-c","5","www.baidu.com"])
child.wait()
print("xxxxxxxxxxxxxxxxxxxx")
运行结果:
import subprocess
child = subprocess.Popen(["ping","-c","5","www.baidu.com"])
child.wait()
print("xxxxxxxxxxxxxxxxxxxx")
PING www.a.shifen.com (115.239.210.27): 56 data bytes
64 bytes from 115.239.210.27: icmp_seq=0 ttl=56 time=5.616 ms
64 bytes from 115.239.210.27: icmp_seq=1 ttl=56 time=3.626 ms
64 bytes from 115.239.210.27: icmp_seq=2 ttl=56 time=4.511 ms
64 bytes from 115.239.210.27: icmp_seq=3 ttl=56 time=4.073 ms
64 bytes from 115.239.210.27: icmp_seq=4 ttl=56 time=142.602 ms
--- www.a.shifen.com ping statistics ---
5 packets transmitted, 5 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 3.626/32.086/142.602/55.262 ms
xxxxxxxxxxxxxxxxxxxx
此外,你还可以在父进程中对子进程进行其它操作,比如我们上面例子中的child对象:
child.poll() # 检查子进程状态
child.kill() # 终止子进程
child.send_signal() # 向子进程发送信号
child.terminate() # 终止子进程
子进程的PID存储在child.pid
- 可以在Popen()建立子进程的时候改变标准输入、标准输出和标准错误,
并可以利用subprocess.PIPE将多个子进程的输入和输出连接在一起,构成管道(pipe):
import subprocess
child1 = subprocess.Popen(["ls","-l"], stdout=subprocess.PIPE)
child2 = subprocess.Popen(["wc"], stdin=child1.stdout,stdout=subprocess.PIPE)
out = child2.communicate()
print(out)
subprocess.PIPE实际上为文本流提供一个缓存区。
child1的stdout将文本输出到缓存区,随后child2的stdin从该PIPE中将文本读取走。
child2的输出文本也被存放在PIPE中,直到communicate()方法从PIPE中读取出PIPE中的文本。
要注意的是,communicate()是Popen对象的一个方法,该方法会阻塞父进程,直到子进程完成。
>>> child1 = subprocess.Popen(["ls","-l"], stdout=subprocess.PIPE)
>>> print child1
<subprocess.Popen object at 0x7f112a1366d0>
>>>
>>> print (child1)
<subprocess.Popen object at 0x7f112a1366d0>
>>>
>>>
>>> out = child1.communicate()
>>> print out
('total 48\n-rw-r--r-- 1 root root 5383 Nov 22 19:21 all.py\n-rw-r--r-- 1 root root 173 Nov 23 11:16 basics.py\n-rwxr-xr-x 1 root root 4164 Nov 21 13:59 get_linux_status.py\n-rw-r--r-- 1 root root 345 Nov 22 16:01 hostname.py\n-rw-r--r-- 1 root root 180 Nov 22 16:06 local_ip.py\n-rwxr-xr-x 1 root root 3198 Nov 22 14:58 mysql_status.py\n-rw-r--r-- 1 root root 10786 Nov 23 16:05 prog.py\n-rw-r--r-- 1 root root 269 Nov 23 16:43 supro.py\n', None)
>>>