# subprocess - Subprocesses with accessible I/O streams
This module allows you to spawn processes, connect to their
input/output/error pipes, and obtain their return codes.
run(...): Runs a command, waits for it to complete, then returns aCompletedProcess instance.
Popen(...): A class for flexibly executing a command in a new process.
subprocess模块主要用于创建子进程,并连接它们的输入、输出和错误管道,获取它们的返回状态。通俗地说就是通过这个模块,你可以在Python的代码里执行操作系统级别的命令,比如“ipconfig”、“du -sh”等等。subprocess模块替代了一些老的模块和函数,比如:os.system,os.spawn*
subprocess.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 可以通过许多参数详细定制子进程的环境,但是只有一个参数是必须的,即位置参数 args .
参数介绍:
- 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 进程。
这个等待和回收子进程的操作就是wait()函数
args可以是字符串也可是是逗号分隔的字符串列表;
在Linux环境下,当args是个字符串时,必须指定shell=True。成功执行后,返回一个subprocess.Popen对象
当args是一个['du', '-sh']
列表,并且shell=True
的时候,参数被忽略了,只执行不带参数的‘du’命令。
创建一个子进程,然后执行一个简单的命令
>>> import subprocess
没有使用标准输出管道
p = subprocess.Popen(['ls', '-l'],shell=True)
>>> p = subprocess.Popen('ls -l',shell=True)
>>> total 10144
-rw-------. 1 root root 1338 Jul 24 17:56 anaconda-ks.cfg
-rw-r--r--. 1 root root 141 Aug 21 11:15 dastudiodb.properties
-rw-r--r--. 1 root root 160 Aug 3 17:12 hostname.txt
-rw-r--r--. 1 root root 271 Aug 3 17:12 hosts_linux.sh
-rw-r--r--. 1 root root 9520728 Aug 13 16:32 node_exporter-1.0.1.linux-amd 64.tar.gz
drwxr-xr-x. 2 root root 6 Jul 27 09:57 perl5
drwxr-xr-x. 2 root root 24 Sep 25 15:30 pip
drwxr-xr-x. 2 root root 25 Sep 17 16:11 prestoadmin
-rw-r--r--. 1 root root 827795 Sep 27 13:49 prestoadmindir.tar.gz
-rw-r--r--. 1 root root 852 Aug 3 17:12 ssh_config.sh
-rw-r--r--. 1 root root 12 Aug 5 09:49 suhadoop.sh
-rw-r--r--. 1 root root 23 Sep 29 14:07 testfile.txt
-rw-r--r--. 1 root root 1063 Aug 5 10:00 zookeeper.out
使用标准输出管道
p = subprocess.Popen('ls -l',stdout=subprocess.PIPE,shell=True)
>>> p = subprocess.Popen('ls -l',stdout=subprocess.PIPE,shell=True)
>>> print(p.stdout.read().decode("utf-8"))
total 10144
-rw-------. 1 root root 1338 Jul 24 17:56 anaconda-ks.cfg
-rw-r--r--. 1 root root 141 Aug 21 11:15 dastudiodb.properties
-rw-r--r--. 1 root root 160 Aug 3 17:12 hostname.txt
-rw-r--r--. 1 root root 271 Aug 3 17:12 hosts_linux.sh
-rw-r--r--. 1 root root 9520728 Aug 13 16:32 node_exporter-1.0.1.linux-amd 64.tar.gz
drwxr-xr-x. 2 root root 6 Jul 27 09:57 perl5
drwxr-xr-x. 2 root root 24 Sep 25 15:30 pip
drwxr-xr-x. 2 root root 25 Sep 17 16:11 prestoadmin
-rw-r--r--. 1 root root 827795 Sep 27 13:49 prestoadmindir.tar.gz
-rw-r--r--. 1 root root 852 Aug 3 17:12 ssh_config.sh
-rw-r--r--. 1 root root 12 Aug 5 09:49 suhadoop.sh
-rw-r--r--. 1 root root 23 Sep 29 14:07 testfile.txt
-rw-r--r--. 1 root root 1063 Aug 5 10:00 zookeeper.out
ret = subprocess.Popen("ls",stdout=subprocess.PIPE,shell=True)
print(ret.stdout.read().decode("utf-8"))
anaconda-ks.cfg
dastudiodb.properties
hostname.txt
hosts_linux.sh
node_exporter-1.0.1.linux-amd64.tar.gz
perl5
pip
prestoadmin
prestoadmindir.tar.gz
ssh_config.sh
suhadoop.sh
testfile.txt
zookeeper.out
Popen 对象的方法
p.poll()
检查子进程 p 是否已经终止,返回 p.returncode 属性
p.wait()
等待子进程 p 终止,返回 p.returncode 属性;
注意:
wait() 立即阻塞父进程,直到子进程结束!
p.communicate(input=None)
和子进程 p 交流,将参数 input (字符串)中的数据发送到子进程的 stdin,同时从子进程的 stdout 和 stderr 读取数据,直到EOF。
返回值:
二元组 (stdoutdata, stderrdata) 分别表示从标准出和标准错误中读出的数据。
父进程调用 p.communicate() 和子进程通信有以下限制:
(1) 只能通过管道和子进程通信,也就是说,只有调用 Popen() 创建子进程的时候参数 stdin=subprocess.PIPE,才能通过 p.communicate(input) 向子进程的 stdin 发送数据;只有参数 stout 和 stderr 也都为 subprocess.PIPE ,才能通过p.communicate() 从子进程接收数据,否则接收到的二元组中,对应的位置是None。
(2)父进程从子进程读到的数据缓存在内存中,因此commucate()不适合与子进程交换过大的数据。
注意:
communicate() 立即阻塞父进程,直到子进程结束!
p.send_signal(signal)
向子进程发送信号 signal;
p.terminate()
终止子进程 p ,等于向子进程发送 SIGTERM 信号;
p.kill()
杀死子进程 p ,等于向子进程发送 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()
0
>>> child2.wait()
0
>>> print(out)
(b'14
', None)
这里将子进程 child1 的标准输出作为子进程 child2 的标准输入,父进程通过 communicate() 读取 child2 的标准输出后打印。
subprocess.STDOUT
调用本模块提供的若干函数时,作为 stderr 参数的值,将子进程的标准错误输出打印到标准输出。