背景:
在python下利用subprocess模块实现进程间的通信。 
使用subprocess包中的函数创建子进程的时候,要注意: 
1) 在创建子进程之后,父进程是否暂停,并等待子进程运行。 
2) 函数返回什么 
3) 当returncode不为0时,父进程如何处理。

封装过的高级接口:
启动一个子进程尽量是使用下面的便捷函数。当对于更高级的使用场景它们不能满足需求时,可以使用底层的Popen接口。

subprocess.call(args, *, stdin=None, stdout=None, stderr=None, shell=False)


执行args 描述的命令,父进程等待子进程完成。返回returncode属性。上面的形参参数只是常用的,该函数的完整形式和Popen构造函数相同。 

返回结果p只是一个return code,dir命令的结果是直接打印出来的,并不作为结果返回。如果需要将dir的命令执行结果作为结果返回的话,用subprocess.check_output() 。其中,shell=True参数会让subprocess.call接受字符串类型的变量作为命令,并调用shell去执行这个字符串(如果是以list存储的数组,此时也会被隐式地转为字符串执行),当shell=False是,subprocess.call只接受数组变量作为命令,并将数组的第一个元素作为命令,剩下的全部作为该命令的参数。 

如果使用Python作为主要的控制流并仍然想方便地访问shell功能,例如shell 管道、文件名通配符、环境变量的扩展、访问某个用户的home目录,那么这将非常有用。但是,执行的shell命令如果来自不可信任的输入源,将使得程序容易受到shell注入攻击,一个严重的安全缺陷可能导致执行任意的命令。因为这个原因,在命令字符串是从外部输入的情况下使用shell=True 是强烈不建议的:
注意,以下代码,

mycmd = ["ls","."]
 p = subprocess.call(mycmd, shell=False)
 print p


因为shell参数为false,所以,需要的是一个数组参数,且第二个不能为空。当前目录,也需要用“.”来表示。

 

subprocess.check_call(args, *, stdin=None, stdout=None, stderr=None, shell=False)


父进程等待子进程完成,如果返回码是0则返回,否则抛出CalledProcessError。CalledProcessError对象将把返回码保存在returncode属性中。 
可以通过检查退出信息,如果returncode不为0,则抛出错误subprocess.CalledProcessError,该对象包含有returncode属性,可用try…except…来检查。 
该函数不要使用stdout=PIPE或者stderr=PIPE,因为基于子进程的输出流量可能导致死锁。当需要管道时,使用Popen和它的communicate()方法。

 

subprocess.check_output()


父进程等待子进程完成,子进程的输出作为字节字符串返回。同样,如果返回码非零,它将引发CalledProcessError。

 

底层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()的封装。与上面的封装不同,Popen对象创建后,主程序不会自动等待子进程完成。必须调用对象的wait()方法,父进程才会等待 (也就是阻塞block)。

import subprocess
 p = subprocess.Popen(["ping","-c","5","127.0.0.1"])
 print "parent process"
 print p从运行结果中看到,父进程在开启子进程之后并没有等待进程p的完成,而是直接运行print。 
 对比等待的情况:import subprocess
 p = subprocess.Popen(["ping","-c","5","127.0.0.1"])
p.wait()
 print "parent process"
 print p


 
 
此外,可以在父进程中对子进程进行其他操作,比如上面例子中的p对象: 

p.poll() # 检查子进程状态 
 p.kill() # 终止子进程 
 p.send_signal() # 向子进程发送信号 
 p.terminate() # 终止子进程


子进程的PID存储在p.pid

子进程的文本流控制:
子进程(child进程) 的标准输入,标准输出和标准错误也可以通过如下属性表示: 
child.stdin 
child.stdout 
child.stderr 
在Popen()建立子进程的时候改变标准输入、标准输出和标准错误,并可以利用subprocess.PIPE将多个子进程的输入和输出连接在一起,构成管道(pipe): 
在python中执行命令行窗口下的命令:ls -l | wc

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

此外,还可以利用communicate()方法来使用PIPE给子进程输入,类似于参数传入:

import subprocess
 child1 = subprocess.Popen(["cat"], stdin=subprocess.PIPE, stdout=subprocess.PIPE,shell=True)
 output = child1.communicate("conf.py")
 print output



Popen.communicate与进程交互:将数据发送到标准输出。从标准输出和标准错误读取数据,直至到达文件末尾。等待进程终止。可选的input 参数应该是一个要发送给子进程的字符串,如果没有数据要发送给子进程则应该为None。 
Popen.communicate()返回一个元组(stdoutdata, stderrdata)。 
注意如果你需要发送数据到进程的标准输入,你需要以stdin=PIPE创建Popen对象。类似地,在结果的元组中若要得到非None的数据,你还需要给出stdout=PIPE和/或stderr=PIPE。