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)
posted @ 2018-07-17 11:04  cyreaper  阅读(428)  评论(0编辑  收藏  举报