subprocess 模块
1 简介
从Python 2.4开始,Python引入subprocess模块来管理子进程,以取代一些旧模块的方法:如 os.system、os.spawn*、os.popen*、popen2.*、commands.*不但可以调用外部的命令作为子进程,而且可以连接到子进程的input/output/error管道,获取相关的返回信息。python3也取消了之前在python中常用来获取运行linux命令行结果的commands.*方法,因此,有必要逐渐熟悉新的方法。
FUNCTIONS call(*popenargs, timeout=None, **kwargs) Run command with arguments. Wait for command to complete or timeout, then return the returncode attribute. The arguments are the same as for the Popen constructor. Example: retcode = call(["ls", "-l"]) check_call(*popenargs, **kwargs) Run command with arguments. Wait for command to complete. If the exit code was zero then return, otherwise raise CalledProcessError. The CalledProcessError object will have the return code in the returncode attribute. The arguments are the same as for the call function. Example: check_call(["ls", "-l"]) check_output(*popenargs, timeout=None, **kwargs) Run command with arguments and return its output. If the exit code was non-zero it raises a CalledProcessError. The CalledProcessError object will have the return code in the returncode attribute and output in the output attribute. The arguments are the same as for the Popen constructor. Example: >>> check_output(["ls", "-l", "/dev/null"]) b'crw-rw-rw- 1 root root 1, 3 Oct 18 2007 /dev/null\n' The stdout argument is not allowed as it is used internally. To capture standard error in the result, use stderr=STDOUT. >>> check_output(["/bin/sh", "-c", ... "ls -l non_existent_file ; exit 0"], ... stderr=STDOUT) b'ls: non_existent_file: No such file or directory\n' There is an additional optional argument, "input", allowing you to pass a string to the subprocess's stdin. If you use this argument you may not also use the Popen constructor's "stdin" argument, as it too will be used internally. Example: >>> check_output(["sed", "-e", "s/foo/bar/"], ... input=b"when in the course of fooman events\n") b'when in the course of barman events\n' If universal_newlines=True is passed, the "input" argument must be a string and the return value will be a string rather than bytes. getoutput(cmd) Return output (stdout or stderr) of executing cmd in a shell. Like getstatusoutput(), except the exit status is ignored and the return value is a string containing the command's output. Example: >>> import subprocess >>> subprocess.getoutput('ls /bin/ls') '/bin/ls' getstatusoutput(cmd) Return (status, output) of executing cmd in a shell. Execute the string 'cmd' in a shell with 'check_output' and return a 2-tuple (status, output). The locale encoding is used to decode the output and process newlines. A trailing newline is stripped from the output. The exit status for the command can be interpreted according to the rules for the function 'wait'. Example: >>> import subprocess >>> subprocess.getstatusoutput('ls /bin/ls') (0, '/bin/ls') >>> subprocess.getstatusoutput('cat /bin/junk') (256, 'cat: /bin/junk: No such file or directory') >>> subprocess.getstatusoutput('/bin/junk') (256, 'sh: /bin/junk: not found') run(*popenargs, input=None, timeout=None, check=False, **kwargs) Run command with arguments and return a CompletedProcess instance. The returned instance will have attributes args, returncode, stdout and stderr. By default, stdout and stderr are not captured, and those attributes will be None. Pass stdout=PIPE and/or stderr=PIPE in order to capture them. If check is True and the exit code was non-zero, it raises a CalledProcessError. The CalledProcessError object will have the return code in the returncode attribute, and output & stderr attributes if those streams were captured. If timeout is given, and the process takes too long, a TimeoutExpired exception will be raised. There is an optional argument "input", allowing you to pass a string to the subprocess's stdin. If you use this argument you may not also use the Popen constructor's "stdin" argument, as it will be used internally. The other arguments are the same as for the Popen constructor. If universal_newlines=True is passed, the "input" argument must be a string and stdout/stderr in the returned object will be strings rather than bytes.
2 常用函数
subprocess.call()
>>> subprocess.call(["ls","-l"],shell=False) total 36 -rw-r--r-- 1 root root 238 May 4 11:07 ex01.py -rw-r--r-- 1 root root 590 May 4 14:14 ex02.py -rw-r--r-- 1 root root 593 May 4 14:53 ex03.py -rw-r--r-- 1 root root 668 May 5 10:40 ex04.py -rw-r--r-- 1 root root 608 May 4 16:31 ex05.py -rw-r--r-- 1 root root 588 May 5 15:02 ex06.py -rw-r--r-- 1 root root 1858 May 5 10:41 passwd -rw-r--r-- 1 root root 0 May 5 15:02 passwd.bak -rw-r--r-- 1 root root 0 May 5 15:02 passwd.tmp -rw-r--r-- 1 root root 43 May 5 10:36 samples.bak -rw-r--r-- 1 root root 37 May 5 10:40 samples.txt 0 >>> subprocess.call(["ls","-l"],shell=True) ex01.py ex02.py ex03.py ex04.py ex05.py ex06.py passwd passwd.bak passwd.tmp samples.bak samples.txt 0
**:shell = True /Flase : 当等于False的时候前面的参数必须是序列,等于True的时候,前面的参数必须是字符串。因为 等于True的时候,会把字符串重新置成列表。
subprocess.check_call()
>>> subprocess.check_call(["cat","1.txt","2.txt"]) a b c 1 2 3 0
返回结果同call()方法一致,但若returncode不为0,则抛出异常CalledProcessError。可用try...except...来捕获
注:cat多个文件合成一个文件时,文件连接间用空行分隔了
subprocess.check_output()
result=subprocess.check_output(["cat","1.txt","2.txt"])
返回标准输出的结果,且若returncode不为0,同样抛出CalledProcessError。例如:
>>> subprocess.check_output("exit 1", shell=True) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/local/python3/lib/python3.6/subprocess.py", line 336, in check_output **kwargs).stdout File "/usr/local/python3/lib/python3.6/subprocess.py", line 418, in run output=stdout, stderr=stderr) subprocess.CalledProcessError: Command 'exit 1' returned non-zero exit status 1.
3 底层函数
subprocess.Popen
1)指定stdout = subprocess.PIPE获取标准输出:
>>> res = subprocess.Popen("cat 1.txt 2.txt",shell=True,stdout=subprocess.PIPE) >>> print(res) <subprocess.Popen object at 0x7fd1a419e358> >>> res.communicate() (b'a\nb\nc\n1\n2\n3\n', None)
运行结果为 标准输出,标准错误输出的元组
注意communicate的作用:
>与子进程进行交互。向stdin发送数据,或从stdout和stderr中读取数据。例如:
>>> p=subprocess.Popen("ls",shell=True,stdout=subprocess.PIPE) >>> p.communicate("-1") (b'1.txt\n2.txt\nex01.py\nex02.py\nex03.py\nex04.py\nex05.py\nex06.py\npasswd\npasswd.bak\npasswd.tmp\nsamples.bak\nsamples.txt\n', None)
>Communicate()返回一个元组:(stdoutdata, stderrdata)
>subprocess.PIPE实际提供了一个缓冲区。communicate将数据从缓冲区读出来。
注:如果你设定了stdout = subprocess.PIPE,而子进程非常多时候,容易死锁。
捕获输出结果:
>>> output=subprocess.check_output("ls -l",shell=True) >>> output b'total 12\n-rw-r--r-- 1 root root 6 May 16 15:14 1.txt\n-rw-r--r-- 1 root root 6 May 16 15:14 2.txt\ndrwxr-xr-x 2 root root 4096 May 16 15:45 bak\n' >>> print(output.decode('utf-8')) total 12 -rw-r--r-- 1 root root 6 May 16 15:14 1.txt -rw-r--r-- 1 root root 6 May 16 15:14 2.txt drwxr-xr-x 2 root root 4096 May 16 15:45 bak 以下例子将chek_output()方法执行命令异常时的错误捕获,而避免输出到控制台. >>> try: ... output = subprocess.check_output("lT -l", shell=True, stderr=subprocess.STDOUT) ... except subprocess.CalledProcessError as err: ... print("Command Error", err) ... Command Error Command 'lT -l' returned non-zero exit status 127.
直接处理管道
subprocess.Popen()方法:函数call(), check_call() 和 check_output() 都是Popen类的包装器。直接使用Popen会对如何运行命令以及如何处理其输入输出有更多控制。如通过为stdin, stdout和stderr传递不同的参数。
1.与进程的单向通信
通过Popen()方法调用命令后执行的结果,可以设置stdout值为PIPE,再调用communicate()获取结果
返回结果为tuple. 在python3中结果为byte类型,要得到str类型需要decode转换一下
直接执行命令输出到屏幕 >>> subprocess.Popen("ls -l",shell=True) <subprocess.Popen object at 0x7f2436aa39b0> >>> total 12 -rw-r--r-- 1 root root 6 May 16 15:14 1.txt -rw-r--r-- 1 root root 6 May 16 15:14 2.txt drwxr-xr-x 2 root root 4096 May 16 15:45 bak 不输出到屏幕,输出到变量 >>> res = subprocess.Popen(['echo','"Stdout"'],stdout=subprocess.PIPE) >>> res.communicate() (b'"Stdout"\n', None) >>> res = subprocess.Popen(['ls','l'],shell=True,stdout=subprocess.PIPE) >>> stdout_value = res.communicate() >>> stdout_value (b'1.txt\n2.txt\nbak\n', None) >>> print((stdout_value[0]).decode('utf-8')) 1.txt 2.txt bak 将结果输出到文件 >>> file_handle = open('./test.log','w+') >>> subprocess.Popen("ls -l",shell=True,stdout=file_handle) <subprocess.Popen object at 0x7f24365aada0> >>> subprocess.Popen("cat test.log",shell=True) <subprocess.Popen object at 0x7f24365aae80> >>> total 12 -rw-r--r-- 1 root root 6 May 16 15:14 1.txt -rw-r--r-- 1 root root 6 May 16 15:14 2.txt drwxr-xr-x 2 root root 4096 May 16 15:45 bak -rw-r--r-- 1 root root 0 May 16 15:58 test.log
2 与进程的双向通信
>>> import subprocess >>> res = subprocess.Popen('cat', shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE) >>> msg = 'Hello world'.encode('utf-8') #写入到输入管道 >>> res.stdin.write(msg) 11 >>> stdout_value = res.communicate() >>> stdout_value (b'Hello world', None) # 在需要进行相互交互的输入输出过程也可以使用shtin来实现 # 以下实现打开python3的终端,执行一个print命令 >>> proc = subprocess.Popen(['python3'],stdin=subprocess.PIPE,stdout=subprocess.PIPE, stderr=subprocess.PIPE,) >>> proc.stdin.write('print("helloworld")'.encode('utf-8')) 19 >>> out_value,err_value=proc.communicate() >>> print(out_value) b'helloworld\n' >>> print(err_value) b''
Popen.communicate()方法用于和子进程交互:发送数据到stdin,并从stdout和stderr读数据,直到收到EOF。等待子进程结束。
Popen其它方法
- Popen.pid 查看子进程ID
-
Popen.returncode 获取子进程状态码,0表示子进程结束,None未结束
在使用Popen调用系统命令式,建议使用communicate与stdin进行交互并获取输出(stdout),这样能保证子进程正常退出而避免出现僵尸进程
>>> proc = subprocess.Popen('ls -l', shell=True, stdout=subprocess.PIPE) # 当前子进程ID >>> proc.pid 28906 # 返回状态为None,进程未结束 >>> print(proc.returncode) None # 通过communicate提交后 >>> out_value = proc.communicate() >>> proc.pid 28906 # 返回状态为0,子进程自动结束 >>> print(proc.returncode) 0
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信息。
1 Popen对象 2 Popen对象有以下方法: 3 Popen.poll() 4 检查子进程是否已结束,设置并返回 returncode 属性。 5 Popen.wait() 6 等待子进程结束,设置并返回 returncode 属性。 7 注意:如果子进程输出了大量数据到stdout或者stderr的管道,并达到了系统 pipe的缓存大小的话,子进程会等待父进程读取管道,而父进程此时正wait着的话,将会产生传说中的死锁,后果是非常严重滴。建议使用communicate()来 避免这种情况的发生。 8 Popen.communicate(input=None) 9 和子进程交互:发送数据到stdin,并从stdout和stderr读数据,直到收到EOF。等待子进程结束。可选的input如有 有的话,要为字符串类型。 10 此函数返回一个元组: (stdoutdata, stderrdata) 。 11 注意,要给子进程的stdin发送数据,则Popen的时候,stdin要为PIPE;同理,要可以收数据的话,stdout或者stderr也要为 PIPE。 12 注意:读到的数据会被缓存在内存里,所以数据量非常大的时候要小心了。 13 Popen.send_signal(signal) 14 给子进程发送signal信号量。 15 注意:windows下目前只支持发送SIGTERM,等效于下面的terminate()。 16 Popen.terminate() 17 停止子进程。Posix下是发送SIGTERM信号。windows下是调用TerminateProcess()这 个API。 18 Popen.kill() 19 杀死子进程。Posix下是发送SIGKILL信号。windows下和terminate()无异。 20 Popen.stdin 21 如果stdin参数是PIPE,此属性就是一个文件对象,否则为None。 22 Popen.stdout 23 如果stdout参数是PIPE,此属性就是一个文件对象,否则为None。 24 Popen.stderr 25 如果stderr参数是PIPE,此属性就是一个文件对象,否则为None。 26 Popen.pid 27 子进程的进程号。注意,如果shell参数为True,这属性指的是子shell的进程号。 28 Popen.returncode 29 子程序的返回值,由poll()或者wait()设置,间接地也由communicate()设置。 30 如果为None,表示子进程还没终止。 31 如果为负数-N的话,表示子进程被N号信号终止。(仅限*nux)
生成 管道(“|”)
我们可以在Popen()建立子进程的时候改变标准输入、标准输出和标准错误,并可以利用subprocess.PIPE将多个子进程的输入和输出连接在一起,构成管道(pipe):
1 >>> import subprocess 2 >>> child1 = subprocess.Popen(["ls","-l"], stdout=subprocess.PIPE) 3 >>> child2 = subprocess.Popen(["wc","-l"], stdin=child1.stdout,stdout=subprocess.PIPE) 4 >>> out = child2.communicate() 5 >>> print(out) 6 (b'5\n', None) 7 >>> print((out[0]).decode('utf-8')) 8 5
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对象的一个方法,该方法会阻塞父进程,直到子进程完成。
浙公网安备 33010602011771号