Python subprocess(标准库)使用

Admin 2018-07-12 15:34:59 Python

Python2.4开始,Python引入了subprocess模块来管理子进程,取代旧模块,如:os.system,os.popen等,subprocess不但可以调用外部的命令作为子进程,而且还可以连接到子进程的INPUT/OUTPUT/ERROR管道,获取相关返回信息。

运行python的时候,我们都是在创建并运行一个进程。像Linux进程那样,一个进程可以fork一个子进程,并让这个子进程exec另外一个程序。在Python中,我们通过标准库中的subprocess包来fork一个子进程,并运行一个外部的程序。

subprocess包中定义有数个创建子进程的函数,这些函数分别以不同的方式创建子进程,所以我们可以根据需要来从中选取一个使用。另外subprocess还提供了一些管理标准流(standard stream)和管道(pipe)的工具,从而在进程间使用文本通信

subprocess常用模块:

subprocess.call()  父进程等待子进程完成,返回子进程退出状态(类似Linux exit code)

>>> subprocess.call(["ls", "-l"])
0
>>> subprocess.call("exit 1", shell=True)
1

shell=True是一种安全机制,详解请看:https://docs.python.org/2/library/subprocess.html#frequently-used-arguments

subprocess.check_call()  父进程等待子进程完成,返回0,如果不是0就抛出异常CalledProcessError

>>> subprocess.check_call(["ls", "-l"])
0
>>> subprocess.check_call("exit 1", shell=True)
Traceback (most recent call last):
   ...
subprocess.CalledProcessError: Command 'exit 1' returned non-zero exit status 1

subprocess.check_output()  父进程等待子进程完成,返回子进程的标准输出,如果退出状态不是0就抛出异常CalledProcessError

>>> subprocess.check_output(["echo", "Hello World!"])
'Hello World!\n'
>>> subprocess.check_output("exit 1", shell=True)
Traceback (most recent call last):
   ...
subprocess.CalledProcessError: Command 'exit 1' returned non-zero exit status 1

注意:call()、check_call()和check_output()尽量不要将参数stderr和stdout设置为subprocee.PIPE,这几个函数默认都会等待子进程完成,子进程产生大量的输出数据如果造成管道堵塞,父进程在等待子进程完成可能造成死锁。

subprocess.Popen类

创建并返回一个子进程,并在这个进程中执行指定的程序,实例化Popen可以通过许多参数定制子进程的环境,即位置参数

  • args:要执行的命令或可执行文件的路径。一个由字符串组成的序列(通常是列表),列表的第一个元素是可执行程序的路径,剩下的是传给这个程序的参数,如果没有要传给这个程序的参数,args 参数可以仅仅是一个字符串。

  • bufsize:控制 stdin, stdout, stderr 等参数指定的文件的缓冲,和打开文件的 open()函数中的参数 bufsize 含义相同。

  • executable:如果这个参数不是 None,将替代参数 args 作为可执行程序;

  • stdin:指定子进程的标准输入;

  • stdout:指定子进程的标准输出;

  • stderr:指定子进程的标准错误输出;

对于 stdin, stdout 和 stderr 而言,如果他们是 None(默认情况),那么子进程使用和父进程相同的标准流文件。

父进程如果想要和子进程通过 communicate() 方法通信,对应的参数必须是 subprocess.PIPE(见下文例4);

当然 stdin, stdout 和 stderr 也可以是已经打开的 file 对象,前提是以合理的方式打开,比如 stdin 对应的文件必须要可读等。

  • preexec_fn:默认是None,否则必须是一个函数或者可调用对象,在子进程中首先执行这个函数,然后再去执行为子进程指定的程序或Shell。

  • close_fds:布尔型变量,为 True 时,在子进程执行前强制关闭所有除 stdin,stdout和stderr外的文件;

  • shell:布尔型变量,明确要求使用shell运行程序,与参数 executable 一同指定子进程运行在什么 Shell 中——如果executable=None 而 shell=True,则使用 /bin/sh 来执行 args 指定的程序;也就是说,Python首先起一个shell,再用这个shell来解释指定运行的命令。

  • cwd:代表路径的字符串,指定子进程运行的工作目录,要求这个目录必须存在;

  • env:字典,键和值都是为子进程定义环境变量的字符串;

  • universal_newline:布尔型变量,为 True 时,stdout 和 stderr 以通用换行(universal newline)模式打开,

  • startupinfo:见下一个参数;

  • creationfalgs:最后这两个参数是Windows中才有的参数,传递给Win32的CreateProcess API调用。同 Linux 中创建子进程类似,父进程创建完子进程后,并不会自动等待子进程执行,父进程在子进程之前推出将导致子进程成为孤儿进程,孤儿进程统一由 init 进程接管,负责其终止后的回收工作。

如果父进程在子进程之后终止,但子进程终止时父进程没有进行最后的回收工作,子进程残留的数据结构称为僵尸进程。大量僵尸进程将耗费系统资源,因此父进程及时等待和回收子进程是必要的,除非能够确认自己比子进程先终止,从而将回收工作过渡给 init 进程。

>>> import subprocess
>>> p = subprocess.Popen('ls -l', shell=True)
>>> total 164
-rw-r--r--  1 root root   4 Jul  4 12:25 a.sh
-rw-r--r--  1 root root   4 Jul 11 13:55 b.sh
...
>>> p.returncode
>>> p.wait()
0
>>> p.returncode
0

Popen对象属性:

1、pid 子进程的PID

2、returncode 子进程返回状态:

  •   None:子进程未结束

  •   = 0   :子进程正常退出

  •   > 0   :子进程异常退出

  •   < 0   :子进程信号杀掉

3、stdin、stdout、stderr :

子进程对应的一些初始文件,如果调用Popen()的时候对应的参数是subprocess.PIPE,则这里对应的属性是一个包裹了这个管道的 file 对象

Popen对象方法:

1、poll()  检查子进程是否终止

2、wait() 等待子进程终止

3、communicate(input=None) 和子进程交流,将参数input(字符串) 发送到子进程的stdin,同时从子进程的stdout和stderr读取数据,直到EOF.

返回二元组,分别是标准输出和标准错误输出

父进程调用 p.communicate() 和子进程通信有以下限制:

(1)只能通过管道和子进程通信,也就是说,只有调用 Popen() 创建子进程的时候参数 stdin=subprocess.PIPE,才能通过 p.communicate(input) 向子进程的 stdin 发送数据;只有参数 stout 和 stderr 也都为 subprocess.PIPE ,才能通过p.communicate() 从子进程接收数据,否则接收到的二元组中,对应的位置是None。

(2)父进程从子进程读到的数据缓存在内存中,因此commucate()不适合与子进程交换过大的数据。

注意:communicate() 立即阻塞父进程,直到子进程结束。

4、send_signal() 向子进程发送信号

5、terminate() 终止子进程,等待向子进程发送SIGTERM信号

6、kill() 杀死子进程,等待向子进程发送SIGKILL信号

subprocess.PIPE 属性

作为std*参数的值,为标准流文件打开一个管道

import subprocess
child1 = subprocess.Popen(['ls', '-l'], stdout=subprocess.PIPE)
child2 = subprocess.Popen(['wc', '-l'], stdin=child1.stdout, stdout=subprocess.PIPE)
out = child2.communicate()
child1.wait()
child2.wait()
print(out)

总结:

使用 Popen 可以在Python进程中创建子进程,如果只对子进程的执行退出状态感兴趣,可以调用 subprocess.call() 函数,如果想通过异常处理机制解决子进程异常退出的情形,可以考虑使用 subprocess.check_call() 和 subprocess.check_output。如果希望获得子进程的输出,可以调用 subprocess.check_output(),但 Popen() 无疑是功能最强大的。

subprocess模块的缺陷在于默认提供的父子进程间通信手段有限,只有管道;同时创建的子进程专门用来执行外部的程序或命令。

相关文章
最新推荐