python模块subprocess学习
- subprocess模块介绍
subprocess是python创建子进程的工具,其实和c中的fork出一个子进程,然后在子进程中运行exec执行另外一个进程很类似。
subprocess包中有很多方法创建子进程,这些函数创建子进程的行为不太一样,我们可以更具需求选择不同的方式来创建子进程。
使用subprocess包中的函数创建子进程的时候,要注意:
1) 在创建子进程之后,父进程是否暂停,并等待子进程运行。
2) 函数返回什么
3) 当returncode不为0时,父进程如何处理。
- subprecess.call()
subprocess.call()
父进程等待子进程完成
返回退出信息(returncode,相当于exit code,见Linux进程基础)
subprocess.check_call()
父进程等待子进程完成
返回0
检查退出信息,如果returncode不为0,则举出错误subprocess.CalledProcessError,该对象包含有returncode属 性,可用try...except...来检查(见Python错误处理)。
subprocess.check_output()
父进程等待子进程完成
返回子进程向标准输出的输出结果
检查退出信息,如果returncode不为0,则举出错误subprocess.CalledProcessError,该对象包含有returncode属性和output属性,output属性为标准输出的输出结果,可用try...except...来检查。
1 #include <iostream> 2 #include <unistd.h> 3 4 using namespace std; 5 6 int main(int argc, const char *argv[]) 7 { 8 cout << "Python is powerful" << endl; 9 for (int i = 0; i < argc; i++) 10 { 11 cout << argv[i] << endl; 12 } 13 sleep(10); 14 return 0; 15 } 16 ~
1 #!/usr/bin/env python 2 3 import subprocess 4 5 returnCode = subprocess.call('ls -l',shell=True)
//我们使用了shell=True这个参数。这个时候,我们使用一整个字符串,而不是一个表来运行子进程。Python将先运行一个shell,再用这个shell来解释这整个字符串。
6 print "returnCode:",returnCode 7 8 returnCode = subprocess.call(['ls','-l'])
//我们将程序名(ls)和所带的参数(-l)一起放在一个表中传递给subprocess.call()
9 print "returnCode:",returnCode 10 11 returnCode = subprocess.call(['./app','-a','-b','-c','-d'])
//app也将参数和app本身以一个列表为传递过去
12 print "returnCode:",returnCode
执行结果:
yca@ubuntu:~/Desktop/go$ ./assert.py total 1256 -rwxr-xr-x 1 yca yca 7785 2013-05-07 20:02 app -rw-r--r-- 1 yca yca 221 2013-05-07 20:01 app.cpp -rwxr-xr-x 1 yca yca 217 2013-05-07 20:40 assert.py -rwxr-xr-x 1 yca yca 1256270 2013-04-28 02:30 hello -rw-r--r-- 1 yca yca 396 2013-05-01 19:59 hello.go -rw-r--r-- 1 yca yca 918 2013-05-07 01:08 HelloWorld.go -rw-r--r-- 1 yca yca 556 2013-05-07 02:43 map.go returnCode: 0 Python is powerful ./app -a -b -c -d returnCode: 0
- subprocess.Popen()
上面三个函数都是对subprocess.Popen的封装,这些封装的目的是为了让我们容易使用子进程。当我们想要更个性化我们的需求的时候,就要转向Popen类,该类生成的对象用来代表子进程。
与上面的封装不同,Popen对象创建后,主程序不会自动等待子进程完成。我们必须调用对象的wait()方法,父进程才会等待 (也就是阻塞block):
1 #!/usr/bin/env python 2 3 import subprocess 4 5 returnCode = subprocess.Popen(['./app','-a','-b','-c','-d']) 6 print "parent process"
yca@ubuntu:~/Desktop/go$ ./assert.py parent process yca@ubuntu:~/Desktop/go$ Python is powerful ./app -a -b -c -d
从运行结果中看到,父进程在开启子进程之后并没有等待child的完成,而是直接运行print。
1 #!/usr/bin/env python 2 3 import subprocess 4 5 child = subprocess.Popen(['./app','-a','-b','-c','-d']) 6 returnCode = child.wait() 7 print "returnCode:",returnCode
8 print "parent process"
yca@ubuntu:~/Desktop/go$ ./assert.py Python is powerful ./app -a -b -c -d
returnCode:0 parent process
很明显父进程在等待子进程执行完毕,才开始执行
此外,你还可以在父进程中对子进程进行其它操作,比如我们上面例子中的child对象:
child.poll() # 检查子进程状态
child.kill() # 终止子进程
child.send_signal() # 向子进程发送信号
child.terminate() # 终止子进程
1.Popen.poll():用于检查子进程是否已经结束。设置并返回returncode属性。 2.Popen.wait():等待子进程结束。设置并返回returncode属性。 3.Popen.communicate(input=None):与子进程进行交互。向stdin发送数据,或从stdout和stderr中读取数据。可选参数input指定发送到子进程的参数。Communicate()返回一个元组:(stdoutdata, stderrdata)。注意:如果希望通过进程的stdin向其发送数据,在创建Popen对象的时候,参数stdin必须被设置为PIPE。同样,如果希望从stdout和stderr获取数据,必须将stdout和stderr设置为PIPE。 4.Popen.send_signal(signal):向子进程发送信号。 5.Popen.terminate():停止(stop)子进程。在windows平台下,该方法将调用Windows API TerminateProcess()来结束子进程。 6.Popen.kill():杀死子进程。 7.Popen.stdin:如果在创建Popen对象是,参数stdin被设置为PIPE,Popen.stdin将返回一个文件对象用于策子进程发送指令。否则返回None。 8.Popen.stdout:如果在创建Popen对象是,参数stdout被设置为PIPE,Popen.stdout将返回一个文件对象用于策子进程发送指令。否则返回None。 9.Popen.stderr:如果在创建Popen对象是,参数stdout被设置为PIPE,Popen.stdout将返回一个文件对象用于策子进程发送指令。否则返回None。 10.Popen.pid:获取子进程的进程ID。 11.Popen.returncode:获取进程的返回值。如果进程还没有结束,返回None。 12.subprocess.call(*popenargs, **kwargs):运行命令。该函数将一直等待到子进程运行结束,并返回进程的returncode。文章一开始的例子就演示了call函数。如果子进程不需要进行交互,就可以使用该函数来创建。 13.subprocess.check_call(*popenargs, **kwargs):与subprocess.call(*popenargs, **kwargs)功能一样,只是如果子进程返回的returncode不为0的话,将触发CalledProcessError异常。在异常对象中,包括进程的returncode信息。
子进程的PID存储在child.pid
Popen对象 Popen对象有以下方法: Popen.poll() 检查子进程是否已结束,设置并返回 returncode 属性。 Popen.wait() 等待子进程结束,设置并返回 returncode 属性。 注意:如果子进程输出了大量数据到stdout或者stderr的管道,并达到了系统 pipe的缓存大小的话,子进程会等待父进程读取管道,而父进程此时正wait着的话,将会产生传说中的死锁,后果是非常严重滴。建议使用communicate()来 避免这种情况的发生。 Popen.communicate(input=None) 和子进程交互:发送数据到stdin,并从stdout和stderr读数据,直到收到EOF。等待子进程结束。可选的input如有 有的话,要为字符串类型。 此函数返回一个元组: (stdoutdata, stderrdata) 。 注意,要给子进程的stdin发送数据,则Popen的时候,stdin要为PIPE;同理,要可以收数据的话,stdout或者stderr也要为 PIPE。 注意:读到的数据会被缓存在内存里,所以数据量非常大的时候要小心了。 Popen.send_signal(signal) 给子进程发送signal信号量。 注意:windows下目前只支持发送SIGTERM,等效于下面的terminate()。 Popen.terminate() 停止子进程。Posix下是发送SIGTERM信号。windows下是调用TerminateProcess()这 个API。 Popen.kill() 杀死子进程。Posix下是发送SIGKILL信号。windows下和terminate()无异。 Popen.stdin 如果stdin参数是PIPE,此属性就是一个文件对象,否则为None。 Popen.stdout 如果stdout参数是PIPE,此属性就是一个文件对象,否则为None。 Popen.stderr 如果stderr参数是PIPE,此属性就是一个文件对象,否则为None。 Popen.pid 子进程的进程号。注意,如果shell参数为True,这属性指的是子shell的进程号。 Popen.returncode 子程序的返回值,由poll()或者wait()设置,间接地也由communicate()设置。 如果为None,表示子进程还没终止。 如果为负数-N的话,表示子进程被N号信号终止。(仅限*nux)
http://blog.csdn.net/mr_jj_lian/article/details/6936984
3. 子进程的文本流控制
(沿用child子进程) 子进程的标准输入,标准输出和标准错误也可以通过如下属性表示:
child.stdin
child.stdout
child.stderr
我们可以在Popen()建立子进程的时候改变标准输入、标准输出和标准错误,并可以利用subprocess.PIPE将多个子进程的输入和输出连接在一起,构成管道(pipe):
1 #!/usr/bin/env python 2 3 import subprocess 4 5 child1 = subprocess.Popen(["ls","-l"], stdout=subprocess.PIPE) 6 child2 = subprocess.Popen(["wc"], stdin=child1.stdout,stdout=subprocess.PIPE) 7 out = child2.communicate() 8 print out
child1.stdout-->subprocess.PIPE
child2.stdin<--subprocess.PIPE
child2.stdout-->subprocess.PIPE
相当于将child1.stdout-->child2.stdin->child2.stdout->subprocess.PIPE
subprocess.PIPE实际上为文本流提供一个缓存区。child1的stdout将文本输出到缓存区,随后child2的stdin从该PIPE中将文本读取走。child2的输出文本也被存放在PIPE中,直到communicate()方法从PIPE中读取出PIPE中的文本。
要注意的是,communicate()是Popen对象的一个方法,该方法会阻塞父进程,直到子进程完成。
我们还可以利用communicate()方法来使用PIPE给子进程输入:
1 import subprocess 2 child = subprocess.Popen(["cat"], stdin=subprocess.PIPE) 3 child.communicate("vamei") //()不为空,则写入subprocess.PIPE,为空,则从subprocess.PIPE读取
subprocess.PIPE-->child.stdin
commiuncate相当于写入subprocess.PIPE,然后child从subprocess.PIPE读取
- returnCode
执行子进程后的返回值是从何而来呢?通过exit的返回值得到
1 #!/bin/bash 2 3 echo "hello" 4 exit 1 5 ~
1 #!/usr/bin/env python 2 3 import subprocess 4 5 child = subprocess.Popen(["./shell.sh"], stdout=subprocess.PIPE) 6 returnCode = child.wait() 7 print "returnCode:",returnCode 8 stdout = child.communicate() 9 print stdout
yca@ubuntu:~/Desktop/go$ ./assert.py returnCode: 1 ('hello\n', None)