Python入门之subprocess
subprocess 模块简介
官网:https://docs.python.org/3.6/library/subprocess.html
subprocess模块用来生成子进程,并可以通过管道连接它们的输入/输出/错误,以及获得它们的返回值。
它用来代替多个旧模块和函数:
os.system
os.spawn*
subprocess模块中定义有数个创建子进程的函数,这些函数分别以不同的方式创建子进程,所以我们可以根据需要来从中选取一个使用。另外subprocess还提供了一些管理标准流(standard stream)和管道(pipe)的工具,从而在进程间使用文本通信。
1、subprocess.run()函数:用于执行一个外部命令,但该方法不能返回执行的结果,只能返回执行的状态码: 成功(0) 或 错误(非0)
run()函数是在Python 3.5中添加的,如果在老版本中使用,需要下载并扩展
[root@localhost 20170722]# pip install subprocess.run Collecting subprocess.run Downloading subprocess.run-0.0.8.tar.gz Installing collected packages: subprocess.run Running setup.py install for subprocess.run ... done Successfully installed subprocess.run-0.0.8
使用方法 :subprocess.run(args, *, stdin=None, input=None, stdout=None, stderr=None, shell=False, cwd=None, timeout=None, check=False, encoding=None, errors=None)
常用参数:
-
args
args是所有调用所必需的,应该为一个字符串或一个程序参数序列。通常倾向提供参数序列,因为它允许这个模块来处理任何所需的转义和引用参数(例如,允许文件名中的空格)。如果传递单个字符串,shell必须为True(见下文),否则字符串必须简单地命名要执行的程序而不指定任何参数。
-
stdin、stdout和stderr
stdin、stdout和stderr分别指定执行程序的标准输入,标准输出和标准错误文件句柄。有效值有PIPE、DEVNULL,一个存在的文件描述器(正整数),一个存在的文件对象和None。PIPE表示应该为子进程创建新的管道。DEVNULL表示将使用特殊文件os.devnull。使用默认设置None,则不会发生重定向;子进程的文件句柄将从父进程继承。此外,stderr可以是STDOUT,表示来自子进程的标准错误数据应该捕获到与stdout相同的文件句柄中。
-
shell
如果shell是True,则将通过shell执行指定的命令。如果你使用Python主要是由于它能提供大多数系统shell不能提供的增强的控制流,并且仍然希望方便地访问其他shell功能,如shell管道、文件名通配符、环境变量扩展和扩展〜到用户的主目录,这会很有用。
- check
检查returncode是否为0,不为0则抛出错误
运行args描述的命令。等待命令完成,然后返回一个CompleteProcess实例;
完整的函数形式很大程度上与Popen构造函数相同 —— 除timeout、input和check之外,该函数的所有参数都传递给Popen接口
>>> subprocess.run(["ls", "-l"]) #shell命令ls -l执行结果;此结果并不会被捕获 total 16 -rwxr-xr-x 1 root root 330 Jul 19 06:02 run.py -rw-r--r-- 1 root root 437 Jul 18 23:20 run.pyc -rwxr-xr-x 1 root root 319 Jul 18 23:27 sub.py -rwxr-xr-x 1 root root 155 Jul 18 23:41 test.py #subprocess.run()返回结果 CompletedProcess(args=['ls', '-l'], returncode=0)
>>> subprocess.run("exit 1", shell=True, check=True) #check=True会检查返回的returncode是否为0,若不是0则会报错 #shell=True表示args的字符串以shell原生命令运行 Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/local/lib/python3.6/subprocess.py", line 418, in run output=stdout, stderr=stderr) subprocess.CalledProcessError: Command 'exit 1' returned non-zero exit status 1.
>>> subprocess.run("uname -r",shell=True) 3.10.0-514.el7.x86_64 #shell命令执行结果,标准输出 CompletedProcess(args='uname -r', returncode=0) #subprocess.run()返回结果 #只显示标准输出 >>> subprocess.run("uname -r",shell=True).stdout 3.10.0-514.el7.x86_64 #不显示标准输出,将标准输出通过管道subprocess.PIPE输出 >>> a = subprocess.run("uname -r",shell=True,stdout=subprocess.PIPE) >>> a CompletedProcess(args='uname -r', returncode=0, stdout=b'3.10.0-514.el7.x86_64\n') >>> a.stdout b'3.10.0-514.el7.x86_64\n' #只返回returncode,相当有linux中执行命令后的返回码(echo $?) >>> subprocess.run("uname -r",shell=True,stdout=subprocess.PIPE).returncode 0 #0表示命令执行成功
2、subprocess.call() 函数
功能和subprocess.run()近似,差别在于run函数返回的是一个CompleteProcess实例,call函数返回的只是returncode
#subprocess.call() >>> subprocess.call("ls -l",shell=True) total 16 -rwxr-xr-x 1 root root 330 Jul 19 06:02 run.py -rw-r--r-- 1 root root 437 Jul 18 23:20 run.pyc -rwxr-xr-x 1 root root 319 Jul 18 23:27 sub.py -rwxr-xr-x 1 root root 155 Jul 18 23:41 test.py 0 #subprocess.run() >>> subprocess.run("ls -l",shell=True) total 16 -rwxr-xr-x 1 root root 330 Jul 19 06:02 run.py -rw-r--r-- 1 root root 437 Jul 18 23:20 run.pyc -rwxr-xr-x 1 root root 319 Jul 18 23:27 sub.py -rwxr-xr-x 1 root root 155 Jul 18 23:41 test.py CompletedProcess(args='ls -l', returncode=0)
3、subprocess.check_call() 方法 :错误处理
我们说过call执行返回一个状态码,我们可以通过check_call()函数来检测命令的执行结果,如果不成功将返回 subprocess.CalledProcessError 异常
类似subprocess.run()中check=True参数的功能
#subprocess.run() >>> subprocess.run("la -l",shell=True,check=True) /bin/sh: la: command not found Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/local/lib/python3.6/subprocess.py", line 418, in run output=stdout, stderr=stderr) subprocess.CalledProcessError: Command 'la -l' returned non-zero exit status 127. #subprocess.check_call() >>> subprocess.check_call("la -l",shell=True) /bin/sh: la: command not found Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/local/lib/python3.6/subprocess.py", line 291, in check_call raise CalledProcessError(retcode, cmd) subprocess.CalledProcessError: Command 'la -l' returned non-zero exit status 127.
4、subprocess.check_output():捕获命令执行结果
subprocess.run和subprocess.call不会捕获shell命令执行的结果,要获取命令执行结果需要check_output()函数
>>> p = subprocess.check_output("ls -l",shell=True) >>> p b'total 16\n-rwxr-xr-x 1 root root 330 Jul 19 06:02 run.py\n-rw-r--r-- 1 root root 437 Jul 18 23:20 run.pyc\n-rwxr-xr-x 1 root root 319 Jul 18 23:27 sub.py\n-rwxr-xr-x 1 root root 155 Jul 18 23:41 test.py\n'
5、subprocess.getout(command):直接返回shell命令执行的结果
command是字符串类型的shell原生命令;
#将shell命令ls -l /的执行结果赋值给p >>> p = subprocess.getoutput("ls -l /") >>> p 'total 24\n-rw-r--r-- 1 root root 0 Jul 18 13:03 1\nlrwxrwxrwx. 1 root root 7 Jun 26 15:35 bin -> usr/bin\ndr-xr-xr-x. 4 root root 4096 Jun 26 15:45 boot\ndrwxr-xr-x 20 root root 3200 Jul 18 11:19 dev\ndrwxr-xr-x. 138 root root 8192 Jul 18 13:15 etc\ndrwxr-xr-x. 4 root root 31 Jun 27 05:36 home\nlrwxrwxrwx. 1 root root 7 Jun 26 15:35 lib -> usr/lib\nlrwxrwxrwx. 1 root root 9 Jun 26 15:35 lib64 -> usr/lib64\ndrwxr-xr-x. 2 root root 6 Nov 5 2016 media\ndrwxr-xr-x. 2 root root 105 Jul 18 17:25 mnt\ndrwxr-xr-x. 3 root root 16 Jul 18 13:14 opt\ndr-xr-xr-x 108 root root 0 Jul 18 11:19 proc\ndr-xr-x---. 12 root root 4096 Jul 19 06:02 root\ndrwxr-xr-x 34 root root 900 Jul 18 13:17 run\nlrwxrwxrwx. 1 root root 8 Jun 26 15:35 sbin -> usr/sbin\ndrwxr-xr-x. 2 root root 6 Nov 5 2016 srv\ndr-xr-xr-x 13 root root 0 Jul 18 11:19 sys\ndrwxrwxrwt. 12 root root 281 Jul 19 07:11 tmp\ndrwxr-xr-x. 13 root root 155 Jun 26 15:35 usr\ndrwxr-xr-x. 21 root root 4096 Jul 18 13:13 var' #支持shell管道 >>> p = subprocess.getoutput("ls -l /|wc -l") >>> p '21'
6、subprocess.getstatusoutput():返回returncode和shell命令执行结果组成的一个元组
>>> subprocess.getstatusoutput('ls /bin/ls') (0, '/bin/ls') >>> subprocess.getstatusoutput("ls -l /") (0, 'total 24\n-rw-r--r-- 1 root root 0 Jul 18 13:03 1\nlrwxrwxrwx. 1 root root 7 Jun 26 15:35 bin -> usr/bin\ndr-xr-xr-x. 4 root root 4096 Jun 26 15:45 boot\ndrwxr-xr-x 20 root root 3200 Jul 18 11:19 dev\ndrwxr-xr-x. 138 root root 8192 Jul 18 13:15 etc\ndrwxr-xr-x. 4 root root 31 Jun 27 05:36 home\nlrwxrwxrwx. 1 root root 7 Jun 26 15:35 lib -> usr/lib\nlrwxrwxrwx. 1 root root 9 Jun 26 15:35 lib64 -> usr/lib64\ndrwxr-xr-x. 2 root root 6 Nov 5 2016 media\ndrwxr-xr-x. 2 root root 105 Jul 18 17:25 mnt\ndrwxr-xr-x. 3 root root 16 Jul 18 13:14 opt\ndr-xr-xr-x 107 root root 0 Jul 18 11:19 proc\ndr-xr-x---. 12 root root 4096 Jul 19 06:02 root\ndrwxr-xr-x 34 root root 900 Jul 18 13:17 run\nlrwxrwxrwx. 1 root root 8 Jun 26 15:35 sbin -> usr/sbin\ndrwxr-xr-x. 2 root root 6 Nov 5 2016 srv\ndr-xr-xr-x 13 root root 0 Jul 18 11:19 sys\ndrwxrwxrwt. 12 root root 281 Jul 19 07:11 tmp\ndrwxr-xr-x. 13 root root 155 Jun 26 15:35 usr\ndrwxr-xr-x. 21 root root 4096 Jul 18 13:13 var')
Popen直接处理管道
subprocess.Popen()方法:函数call(), check_call() 和 check_output() 都是基于Popen()的封装(wrapper)。这些封装的目的在于让我们容易使用子进程。当我们想要更个性化我们的需求的时候,就要转向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)
1. wait()方法:与上面的封装不同,Popen对象创建后,主程序不会自动等待子进程完成。我们可以调用对象的wait()方法,父进程才会等待 (也就是阻塞block)
- 不调用wait()方法
#!/usr/bin/env python3 import subprocess child = subprocess.Popen(["ping","-c","5","www.baidu.com"]) print("parent process") 执行结果: [root@localhost 20170722]# ./pipe.py parent process [root@localhost 20170722]# PING www.a.shifen.com (180.97.33.108) 56(84) bytes of data. 64 bytes from 180.97.33.108 (180.97.33.108): icmp_seq=1 ttl=55 time=8.57 ms 64 bytes from 180.97.33.108 (180.97.33.108): icmp_seq=2 ttl=55 time=7.58 ms 64 bytes from 180.97.33.108 (180.97.33.108): icmp_seq=3 ttl=55 time=7.93 ms 64 bytes from 180.97.33.108 (180.97.33.108): icmp_seq=4 ttl=55 time=10.7 ms 64 bytes from 180.97.33.108 (180.97.33.108): icmp_seq=5 ttl=55 time=11.5 ms --- www.a.shifen.com ping statistics --- 5 packets transmitted, 5 received, 0% packet loss, time 4017ms rtt min/avg/max/mdev = 7.585/9.283/11.565/1.584 ms #从运行结果中看到,父进程在开启子进程之后并没有等待child的完成,而是直接运行print。
- 调用wait()方法
#!/usr/bin/env python3 import subprocess child = subprocess.Popen(["ping","-c","5","www.baidu.com"]) child.wait() print("parent process") 执行结果: [root@localhost 20170722]# ./pipe_wait.py PING www.a.shifen.com (180.97.33.107) 56(84) bytes of data. 64 bytes from 180.97.33.107 (180.97.33.107): icmp_seq=1 ttl=55 time=7.15 ms 64 bytes from 180.97.33.107 (180.97.33.107): icmp_seq=2 ttl=55 time=7.73 ms 64 bytes from 180.97.33.107 (180.97.33.107): icmp_seq=3 ttl=55 time=8.76 ms 64 bytes from 180.97.33.107 (180.97.33.107): icmp_seq=4 ttl=55 time=9.95 ms 64 bytes from 180.97.33.107 (180.97.33.107): icmp_seq=5 ttl=55 time=10.8 ms --- www.a.shifen.com ping statistics --- 5 packets transmitted, 5 received, 0% packet loss, time 4017ms rtt min/avg/max/mdev = 7.157/8.894/10.859/1.373 ms parent process #从运行结果中看到,子进程调用wait()函数后;父进程会在子进程child完成后在运行print
注:在调用wait()方法是容易产生死锁现象;使用 subprocess 模块的 Popen 调用外部程序,如果 stdout 或 stderr 参数是 pipe,并且程序输出超过操作系统的 pipe size时,如果使用 Popen.wait() 方式等待程序结束获取返回值,会导致死锁,程序卡在 wait() 调用上。
linux中ulimt -a可以查看到pipe size大小
[root@localhost 20170722]# ulimit -a core file size (blocks, -c) 0 data seg size (kbytes, -d) unlimited scheduling priority (-e) 0 file size (blocks, -f) unlimited pending signals (-i) 7208 max locked memory (kbytes, -l) 64 max memory size (kbytes, -m) unlimited open files (-n) 1024 pipe size (512 bytes, -p) 8 POSIX message queues (bytes, -q) 819200 real-time priority (-r) 0 stack size (kbytes, -s) 8192 cpu time (seconds, -t) unlimited max user processes (-u) 7208 virtual memory (kbytes, -v) unlimited file locks (-x) unlimited
2. communicate():除了wait()方法阻塞父进程外,我们也可以使用communicate()方法;而且使用communicate()方法会避免死锁出现,因为这个方法会把输出放在内存,而不是管道里,所以这时候上限就和内存大小有关了,一般不会有问题。而且如果要获得程序返回值,可以在调用 Popen.communicate() 之后取 Popen.returncode 的值。
communicate()会返回一个元组(stdout,stderr)
#!/usr/bin/env python3 import subprocess child = subprocess.Popen(["ls","-l","/tmp"],stdout=subprocess.PIPE) out = child.communicate() #stdout输出结果 code = child.returncode #returncode值 print(out,"\n",code) 执行结果: [root@localhost 20170722]# ./pipe_communicate.py (b'total 116\ndrwxr-xr-x 2 root root 85 Jul 18 17:32 20170720\ndrwxr-xr-x 2 root root 103 Jul 19 05:58 20170721\ndrwxr-xr-x 2 root root 120 Jul 19 12:35 20170722\ndrwxr-xr-x 2 root root 18 Jul 18 13:08 hsperfdata_root\ndrwxr-xr-x. 2 root root 30 Jun 27 05:27 shutil_file\n-rw------- 1 root root 99005 Jul 18 11:27 yum_save_tx.2017-07-18.11-27.uzI4eR.yumtx\n-rw------- 1 root root 15118 Jul 18 12:53 yum_save_tx.2017-07-18.12-53.vANJxP.yumtx\n', None) 0
参考:Popen.wait()和Popen.communicate()区别
此外,你还可以在父进程中对子进程进行其它操作,比如我们上面例子中的child对象:
- child.poll() # 检查子进程状态
- child.kill() # 终止子进程
- child.send_signal() # 向子进程发送信号
- child.terminate() # 终止子进程
- 子进程的PID存储在child.pid
3. 子进程文本流控制
(沿用child子进程) 子进程的标准输入,标准输出和标准错误也可以通过如下属性表示:
child.stdin
child.stdout
child.stderr
我们可以在Popen()建立子进程的时候改变标准输入、标准输出和标准错误,并可以利用subprocess.PIPE将多个子进程的输入和输出连接在一起,构成管道(pipe):
#!/usr/bin/env python3 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) 执行结果: [root@localhost 20170722]# ./popen_pipe.py (b' 8 65 354\n', None)
subprocess.PIPE实际上为文本流提供一个缓存区。child1的stdout将文本输出到缓存区,随后child2的stdin从该PIPE中将文本读取走。child2的输出文本也被存放在PIPE中,直到communicate()方法从PIPE中读取出PIPE中的文本。

参考网址:http://www.cnblogs.com/sunailong/p/5162748.html
http://www.cnblogs.com/vamei/archive/2012/09/23/2698014.html
***********************************************************
学习永远不晚。——高尔基
***********************************************************

浙公网安备 33010602011771号