python 标准类库-并行执行之subprocess-子进程管理
标准类库-并行执行之subprocess-子进程管理
by:授客QQ:1033553122
1.使用subprocess模块
以下函数是调用子进程的推荐方法,所有使用场景它们都能处理。也可用Popen以满足更高级的使用场景
subprocess.call(args, *, stdin=None, stdout=None, stderr=None, shell=False, timeout=None)
运行args描述的命令,等待命令完成后返回returncode属性。
timeout参数会传递Popen.wait()。如果超过timeout,子进程将会被kill掉,并再次等待。子进程被终止后会抛出TimeoutExpired异常。
Eg:
>>>returncode = subprocess.call('exit 1', shell=True)
print(returncode)# 输出1
>>> returncode = subprocess.call('exit 0', shell=True)
print(returncode)# 输出0
注意:针对该函数,不要使用stdout=PIPE 或 stderr=PIPE。因为不是从当前进程中读取管道(pipe),如果子进程没有生成足够的输出来填充OS的管道缓冲区,可能会阻塞子进程。
subprocess.check_call(args, *, stdin=None, stdout=None, stderr=None, shell=False, timeout=None)
运行携带参数的命令,等待命令完成。如果返回代码为0,则返回,否则抛出 CalledProcessError。返回代码将被赋值给CalledProcessError的returncode属性
timeout参数会传递Popen.wait()。如果超过timeout,子进程将会被kill掉,并再次等待。子进程被终止后会抛出TimeoutExpired异常。
Eg:
>>> subprocess.check_call(["ls", "-l"]) # run on linux only
0
>>> subprocess.check_call('exit 0', shell=True)
0
>>> subprocess.check_call('exit 1', shell=True)
Traceback (most recent call last):
……
subprocess.CalledProcessError: Command 'exit 1' returned non-zero exit status 1
注意:针对该函数,不要使用stdout=PIPE 或 stderr=PIPE。因为不是从当前进程中读取管道(pipe),如果子进程没有生成足够的输出来填充OS的管道缓冲区,可能会阻塞子进程。
subprocess.check_output(args, *, stdin=None, stderr=None, shell=False, universal_newlines=False, timeout=None)
运行携带参数的命令,并返回输出
如果返回代码为b不为0,则抛出 CalledProcessError。返回代码将被赋值给CalledProcessError的returncode属性,且任意输出将会被存放在output属性。
timeout参数会传递Popen.wait()。如果超过timeout,子进程将会被kill掉,并再次等待。子进程被终止后会抛出TimeoutExpired异常。
Eg:
>>> subprocess.check_output(['echo', 'hello world'], shell=True)
b'"hello world"\r\n'
>>> subprocess.check_output(['echo', 'hello world'], universal_newlines=True, shell=True)
'"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
>>>
默认的,该函数会返回编码的字节。实际输出的编码可能依赖被调用的命令。 所以,对于输出text的解码经常需要在应用层处理。可通过设置universal_newlines 为True来覆盖编码行为。
也可以通过使用stderr=subprocess.STDOUT在结果中捕获标准错误。
Eg:
>>> subprocess.check_output(
'dir non_existent_dir | exit 0',
stderr=subprocess.STDOUT,
universal_newlines=True,
shell=True)
'找不到文件\n'
注意:针对该函数,不要使用stderr=PIPE。因为不是从当前进程中读取管道(pipe),如果子进程没有生成足够的输出来填充OS的管道缓冲区,可能会阻塞子进程。
subprocess.DEVNULL
可用于Popen函数stdin,stdout或者stderr参数的特定值,表示使用指定文件os.devnull
subprocess.PIPE
可用于Popen函数stdin,stdout或者stderr参数的指特定值,表示必须打开一个指向标准流的管道。
subprocess.STDOUT
可用于Popen函数stdin,stdout或者stderr参数的指特定值,表示标准错误信息必须一起写入同样的句柄,比如标准输出。
exception subprocess.SubprocessError
来自该模块的所有异常的父类。
exception subprocess.TimeoutExpired
SubprocessError的子类,当等待子进程timeout超时抛出
cmd
用于衍生子进程的命令。
timeout
以秒wield单位的超时时间。
output
如果异常由check_output抛出,则存放子进程的输出。否则None
exception subprocess.CalledProcessError
SubprocessError的子类,当check_call() 或check_output()运行的进程退出时,返回非0值时抛出。
returncode
子进程的退出状态
cmd
用于衍生子进程的命令。
output
如果异常由check_output抛出,则存放子进程的输出。否则None
2.频繁使用的参数
以下是Popen,call,check_call,check_output等函数最常使用的参数:
args 所有调用的必填参数,参数值为字符串、序列。处于方便,通常更偏向于提供序列。如果传递的是单一字符串,shell参数值必须为True,如果不提供其它任何参数,传递单一字符串的情况下,该字符串必须是需要执行的程序名。
stdin, stdout,stderr分别指明了被执行程序的标准输入,标准输出和标准错误处理文件句柄。可选值PIPE,DEVNULL,已存在文件描述符(一个正整数),已存在文件对象,None。PIPE表示应该创建通往子进程的管道。DEVNULL表示应该使用指定文件os.devnull。默认参数None则表示无进行重定向,子进程文件句柄从父进程继承。此外,stderr还可以是STDOUT,表明子进程的错误数据应该被放进相同的文件句柄stdout
如果universal_newlines为True,文件对象stdin,stdout,stderr将按universal newlines(Unix 行结束符:'\n', Windows行结束符:'\r\n')模式,使用locale.getpreferredencoding(false)(函数会根据用户偏好设置,返回使用的文本数据的编码)返回的编码,以文件流的方式打开。
如果shell为True,指定命令将通过shell执行。出于安全考虑,如果命令字符串参数需要通过外部的输入来构成的时候,强烈建议设置shell=False,不然容易造成shell注入之类的,如下
from subprocess import call
if __name__ == '__main__':
dirname = input('which dir would you like to cd in?\n')
call('cd ' + dirname, shell=True)
运行结果
3. Popen构造器
class subprocess.Popen(args, bufsize=-1, executable=None, stdin=None, stdout=None, stderr=None, preexec_fn=None, close_fds=True, shell=False, cwd=None, env=None, universal_newlines=False, startupinfo=None, creationflags=0, restore_signals=True, start_new_session=False, pass_fds=())
在一个新进程中执行子进程。类似在Unix上使用os.execvp(),Windows上使用CreateProcess()函数。
args 参数值为字符串、序列。默认的,如果args是个序列,程序会执行args中第一项。如果args是字符串则根据平台而异,如下所述。无特殊需求,建议传递序列。
注意:可用shlex.split()来判断正确的args分割,特别是复杂的情况
Eg:
>>> import shlex, subprocess
>>> command_line = input()
/bin/vikings -input eggs.txt -output "spam spam.txt" -cmd "echo '$MONEY'"
>>> args = shlex.split(command_line)
>>> print(args)
['/bin/vikings', '-input', 'eggs.txt', '-output', 'spam spam.txt', '-cmd', "echo '$MONEY'"]
>>> subprocess.Popen(args)
在Windows上,如果args为序列,那么将会按照以下规则进行转换为一个字符串,因为后台函数 CreateProcess() 的操作依赖字符串。
- 参数由空白字符(空格或tab)分隔。
- 通过双引号标记的字符串被解释为单个参数,不管字符串中是否包含空白字符。
- \"被当作是字面字符 ",即转义字符
- 除非\后面跟了双引号,如\",否则还是被解释为字面字符 \
- \\被解释为\,\\\"则被解释为一个 \和一个 " 字符
shell参数用于指明是否使用shell作为执行程序。默认False。如果sell为True,则推荐传递字符串参数给args
Unix操作系统上,shell=True,shell默认为/bin/sh。如果args为字符串,则字符串指明了需要通过shell执行的命令。这意味着字符串必须具备准确的格式,正如在shell终端中输入的一样。如果args为序列,则第一项指定命令字符串,其它额外项则被当作额外参数,等同于说
Popen(['/bin/sh', '-c', args[0], args[1], ...])
Windows如果shell=True,COMSPCE环境变量指定了默认的shell。仅在command 命令需通过shell执行,比如dir,copy命令时,使用shell=True。不必要通过设置shell=True,来运行一批处理或者基于控制的可执行程序。
bufsize 当创建stdin/stdout/stderr管道文件对象时,bufsize将作为io.open()函数的对应的参数: 0 - 意味着未缓冲 (means unbuffered (read and write are one system call and can return short))
1 - 意味着行缓冲(means line buffered)
任意正数 - 使用缓冲,缓冲大小和给定正数大致相等。
任意负数 - 使用缓冲,缓冲大小等于系统自带的o.DEFAULT_BUFFER_SIZE
Executable
executable参数指定了用于执行的替代程序。很少用到。
stdin, stdout 和stderr
分别指定被执行程序的标准输入,标准输出,标准错误文件句柄。合法值为PIPE,DEVNULL,已存在文件描述符(一个正整数),已存在文件对象和None。 PIPE表示应该创建通往子进程的管道。DEVNULL表示应该使用指定文件os.devnull。默认参数None则表示无进行重定向,子进程文件句柄从父进程继承。此外,stderr还可以是STDOUT,表明子进程的错误数据应该被放进相同的文件句柄stdout
preexec_fn
如果preexec_fn 被设置为可调用对象,该对象将在子进程执行之前被执行(仅限Unix)。
close_fds
如果close_fds为True, 所有文件描述符,0,1,2除外都在子进程执行前被关闭(仅限Unix)。 (Unix only). 默认值根据平台而异。Unix平台总是True。Windows平台,当stdin/stdout/stderr为None时,为True,否则为False。Windows平台,如果close_fds为True,那么子进程不会继承任何句柄。
universal_newlines
如果universal_newlines为True,文件对象stdin,stdout,stderr将按universal newlines(Unix 行结束符:'\n', Windows行结束符:'\r\n')模式,使用locale.getpreferredencoding(false)(函数会根据用户偏好设置,返回使用的文本数据的编码)返回的编码,以文件流的方式打开。
……
可配合with使用,退出时,先关闭标准文件描述符,如下
import subprocess
if __name__ == '__main__':
with subprocess.Popen(['dir'], stdout=subprocess.PIPE, shell=True, universal_newlines = True) as proc:
print(proc.stdout.read())
输出
更多参考官方文档
4.Popen对象
Popen类实例有以下几个方法
Popen.poll()
检测子进程是否中断,设置并返回returncode
Popen.wait(timeout=None)
等待子进程终止,设置并返回returncode。如果进程在timeout(单位 秒)之后依然没终止,则抛出TimeoutExpired 异常,可以捕获该异常并再次尝试等待。
警告
当使用stdout=PIPE and/or stderr=PIPE时,如果子进程生成足够的输出到管道,这会阻止操作系统管道缓冲区接收更多数据,进而造成死锁。为了避免该事件,使用communicate()
Popen.communicate(input=None, timeout=None)
和process交互:发送数据到stdin,从stdout,stderr读取数据,直到文件结束符。等待子进程终止。
input:可选参数,参数值为发送给子进程的数据,如果不需要发送数据,则为None。如果universal_newlines为False,则input数据类型必须为字节,否则可为字符串。
函数返回一个元组(stdoutdata, stderrdata)
注意,如果想发送数据到进程管道,必须在创建Popen对象时使用stdin=PIPE,类似的如果想从结果元组中获取非None值数据,创建Popen对象时需要提供stdout=PIPE and/or stderr=PIPE参数。
如果进程在timeout(单位 秒)之后依然没终止,则抛出TimeoutExpired 异常,(Python3.3.2中发丝。捕获该异常并重试comunicate,不会丢失任何输出。
如果超过timeout,子进程不会被kill掉,所以为了完成交互,恰当的清理友好执行的程序,应该kill子进程。
import subprocess
if __name__ == '__main__':
with subprocess.Popen(['dir'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, universal_newlines = True) as proc:
try:
outs, errs = proc.communicate(timeout=15) #超时时间为15秒
print(outs, errs)
except subprocess.TimeoutExpired:
proc.kill()
outs, errs = proc.communicate()
print(outs, errs)
注意:读取的数据缓存在内存,所以如果数据太大或者无限制,不要使用该函数。
Popen.send_signal(signal)
发送signal给子进程
Popen.terminate()
停止子进程。
Popen.kill()
Kill子进程。 Posix操作系统:函数会发送SIGKILL给子进程。Windows,kill()为terminate()别名。
以下为属性:
注意:使用communicate() 而非.stdin.write, .stdout.read 或者.stderr.read以避免死锁。
Popen.stdin
如果stdin参数为PIPE,该属性为给子进程提供输入的文件对象, 否则为None.
Popen.stdout
如果stdin参数为PIPE,该属性为给子进程提供正确输出的文件对象,否则为None.
Popen.stderr
如果stdin参数为PIPE,该属性为给子进程提供错误输出的文件对象,否则为None.
Popen.pid
子进程的ID。
注意:如果设置了shell=True,则该属性值为衍生的shell进程的id
Popen.returncode
子进程返回代码,如果值为None表明进程还没终止。负值-N表示子进程通过signal N终止的(仅限Unix)
Eg:
import sys
import subprocess
def run_command():
cmd = [sys.executable, 'py1.py']
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, universal_newlines = True, stderr=subprocess.STDOUT)
outs, errs = proc.communicate()
# print(proc.communicate()) #报错,因为文件已经关闭
print(outs) if __name__ == '__main__':
run_command()
运行结果:
注:py1.py和study.py在同一个目录下,内容如下
#!/usr/bin/env python
# -*- coding:utf-8 -*- __author__ = 'laiyu' print('output from py1.py')
5.替换老函数
可用call、Popen替换一些老函数如os.system(),os.spawn家族系列,shell管道等,具体参考官方文档
python 标准类库-并行执行之subprocess-子进程管理的更多相关文章
- Python 标准类库- 因特网协议于支持之UUID
标准类库- 因特网协议于支持之UUID by:授客 QQ:1033553122 测试环境 python3 UUID生成函数定义 uuid.getnode() 获取一个表示硬件地址的48位正整数.第 ...
- Python 标准类库 - 因特网协议与支持之socketserver
标准类库 - 因特网协议与支持之socketserver by:授客 QQ:1033553122 socketserver 模块,简化网络服务编写任务. 创建服务的步骤 1 通过子类化BaseReq ...
- Python 标准类库-数据类型之copy-深拷贝浅拷贝操作
标准类库-数据类型之copy-深拷贝浅拷贝操作 by:授客 QQ:1033553122 Python中赋值并不会拷贝对象,只是创建目标和对象的绑定关系. copy.copy(x) 返回x的浅拷贝 ...
- Python 标准类库-Windows特殊服务之msvcrt
标准类库-Windows特殊服务之msvcrt by:授客 QQ:1033553122 广告:出售自研自动化小平台(无需编码也可用),有需要请联系 测试环境 win7 64位 Python 3.4 ...
- python 标准类库-数据类型之集合-容器数据类型
标准类库-数据类型之集合-容器数据类型 by:授客 QQ:1033553122 Counter对象 例子 >>> from collections import Counter ...
- Python 标准类库-日期类型之datetime模块
标准类库-日期类型之datetime模块 by:授客 QQ:1033553122 可用类型 3 实践出真知 4 timedelta对象 4 class datetime.timedelta(da ...
- Python 标准类库-数字和数学模块之decimal使用简介
标准类库-数字和数学模块之decimal使用简介 by:授客 QQ:1033553122 例子 >>>from decimal import * >>>getcon ...
- 转--Python标准库之一句话概括
作者原文链接 想掌握Python标准库,读它的官方文档很重要.本文并非此文档的复制版,而是对每一个库的一句话概括以及它的主要函数,由此用什么库心里就会有数了. 文本处理 string: 提供了字符集: ...
- Python标准库06 子进程 (subprocess包)
这里的内容以Linux进程基础和Linux文本流为基础.subprocess包主要功能是执行外部的命令和程序.比如说,我需要使用wget下载文件.我在Python中调用wget程序.从这个意义上来说, ...
随机推荐
- AI - 学习路径(Learning Path)
初见 机器学习图解 错过了这一篇,你学机器学习可能要走很多弯路 这3张脑图,带你清晰人工智能学习路线 一些课程 Andrew Ng的网络课程 HomePage:http://www.deeplearn ...
- wsgiref源码解析
wsgiref是PEP 333定义的wsgi规范的范例实现,里面的功能包括了: wsgi的环境变量 应答头部的处理 实现简单的HTTP服务器 简单的对程序端和服务器端校验函数 我们先看一个简单的代码实 ...
- 剑指offer【03】- 从尾到头打印链表(4种实现方法)
题目:从尾到头打印链表 考点:链表 题目描述:输入一个链表,按链表值从尾到头的顺序返回一个ArrayList. 法一:ArrayList头插法 /** * public class ListNode ...
- 【ABP框架系列学习】启动配置(5)
ABP提供了在启动时配置模块的基础设施和模型. 1.配置ABP 配置ABP是在模块的PreInitialize方法中完成的,例如: public class SimpleTaskSystemModul ...
- springBoot(5)---单元测试,全局异常
单元测试,全局异常 一.单元测试 1.基础版 1.引入相关依赖 <!--springboot程序测试依赖,如果是自动创建项目默认添加--> <dependency> <g ...
- Java垃圾回收(GC)机制详解
一.为什么需要垃圾回收 如果不进行垃圾回收,内存迟早都会被消耗空,因为我们在不断的分配内存空间而不进行回收.除非内存无限大,我们可以任性的分配而不回收,但是事实并非如此.所以,垃圾回收是必须的. 二. ...
- list源码4(参考STL源码--侯捷):transfer、splice、merge、reverse、sort
list源码1(参考STL源码--侯捷):list节点.迭代器.数据结构 list源码2(参考STL源码--侯捷):constructor.push_back.insert list源码3(参考STL ...
- sql server 性能调优之 资源等待 LCk
一. 概述 这次介绍实例级别资源等待LCK类型锁的等待时间,关于LCK锁的介绍可参考 “sql server 锁与事务拨云见日”.下面还是使用sys.dm_os_wait_stats 来查看,并找出 ...
- Spring概况(一)
spring是什么? spring是一个开源框架,最初是为了解决企业应用开发的复杂性而创建的,但现在已经不止应用于企业应用. 是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架. - 从大 ...
- Python快速学习04:循环 & 函数
前言 系列文章:[传送门] 也就今天认识了 LC ,很开心. 本文目录 循环 for while 中断 函数 函数定义 函数调用 for循环 Python 中的for 循环象shell 脚本里的for ...