Python Module_subprocess_子进程(程序调用)
目录
前言
subpocess用于在父进程中创建子进程,如果你希望在Python程序中调用外部程序,如:Powershell、shell、cmd、bat。subprocess将会是一个非常好的选择。
软件环境
- 系统
- Win 10
- 软件
- Python 3.4.4
- IPython 4.0.0
认识subprocess
还是那句话,最高效的方法不过看官方文档,传送门:这里
subprocess:The subprocess module allows you to spawn new processes, connect to their input/output/error pipes, and obtain their return codes. This module intends to replace several older modules and functions:
os.system
os.spawn*
os.popen*
popen2.*
commands.*
subprocess的诞生是为了替代、整合以前几种旧的创建子进程的方法,能够实现以管道的形式连接子进程的stdin、stdout、stderr,并且子进程会返回一个returncode给父进程。与C语言中的fock实现类似的功能。
Execute a child program in a new process. On POSIX, the class uses os.execvp() like behavior to execute the child program. On Windows, the class uses the Windows CreateProcess() function
在POSIX类型系统中会使用os.execvp()
来执行子程序,而在windows环境中会使用CreateProcess()
函数来执行,这篇博文主要记录在windows环境下的使用。
Popen Constructor(构造函数)
subprocess拥有数个通过不同的方式来创建子进程的函数,但是subprocess只有一个Popen类,同样是用于创建子进程。使用Class Popen创建的对象拥有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=())
在Python 3.2之后的版本Popen对象添加了下面这种写法:
on exit, standard file descriptors are closed, and the process is waited for.
with Popen(["ifconfig"], stdout=PIPE) as proc:
log.write(proc.stdout.read())
下面介绍Popen类的参数含义。
Class Popen的参数
args
args :should be a sequence of program arguments or else a single string.
args参数可以是String类型或者sequence类型,作为子程序的声明。在Windows中调用的子进程API是CreateProcess([String]),所以可以接受诸如notepad.exe test.txt
这样的字符串来执行。但是在Linux的环境下需要接受List类型对象来分隔程序名和参数。如果是序列类型,序列的第一个参数一般也作为子程序的路径,之后的元素作为传入子程序的参数。官方建议args参数使用List类型对象。
调用程序
调用一个Powershell脚本程序:
args = [r"C:\WINDOWS\system32\WindowsPowerShell\v1.0\powershell.exe","-ExecutionPolicy","Unrestricted",r"E:\Users\oe-fanguiju\Desktop\SendMail.ps1",str(bodyStr)]
ps = subprocess.Popen(args,stdout=subprocess.PIPE)
注意:在String前加入r
是为了避免出现SyntaxError: (unicode error)
调用Shell指令
序列化传入参数 shlex.split():
我们还可以使用shlex.split()函数来将我们所需要执行的指令序列化后再赋值给args参数。
>>> 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'"]
>>> p = subprocess.Popen(args) # Success!
stdin\stdout\stderr
stdin\stdout\stderr指定了子程序的标准输入、输出、错误的文件句柄(file handles)。他们可以是PIPE管道、DEVNULL(DEVNULL indicates that the special file os.devnull will be used.)、文件描述符(existing file descriptor 一个正整数)或已存在的文件对象(file object)。当他的值是None时,不会发生重定向,子进程的文件句柄将会继承父进程。而且stderr=subprocess.STDOUT能够将标准错误的文件句柄设成标准输出(子程序的标准错误汇合到标准输出)。将stdout/stderr指定为subprocess.PIPE,这样在Popen被调用的时候会在父进程和子进程之间建立管道,子进程的标准输出和错误输出都重定向到管道,可以被父进程获取。
实时获取子程序输出
p = subprocess.Popen("/etc/service/tops-cmos/module/hadoop/test.sh", shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
returncode = p.poll()
while returncode is None: #检查子程序是否结束
line = p.stdout.readline() #若没有,则获取子程序的输出
returncode = p.poll()
line = line.strip()
print line
print returncode
这样就可以实时的获取子进程的输出。
一次获取子程序的全部输出
当你希望在子程序执行完后一次性获取所有子进程输出时,子进程对象可以调用communicate(),他会一直阻塞,等待子进程结束后获取子进程返回的输出。
ps = subprocess.Popen(args,stdout=subprocess.PIPE)
psAllReturn = ps.communicate()
#Or: psReturn = ps.stdout.read()
return psReturn
只有当子程序执行结束的后才会返回执行结果。
注意:communicate()会在通信一次之后即关闭了管道。如果希望进程之间频繁的通信,并不建议这种方法。
可以尝试下面的方法:
p= subprocess.Popen(["wc"], stdin=subprocess.PIPE,stdout=subprocess.PIPE,shell=True)
p.stdin.write('your command') #传递数据给子进程
p.stdin.flush() #清空子进程管道缓存
#......do something
try:
#......do something
p.stdout.readline() #获取子进程输出
#......do something
except:
print('IOError')
#......do something more
p.stdin.write('your other command')
p.stdin.flush()
#......do something more
将标准错误和标准输出一起输出
ps = subprocess.Popen(args,stdout=subprocess.PIPE,stderr=subprocess.STDOUT)
这样无论是标准输出还是标准错误输出都会经过stdout管道返回给父进程。
shell
The shell argument (which defaults to False) specifies whether to use the shell as the program to execute. If shell is True, it is recommended to pass args as a string rather than as a sequence.
shell指定是否使用shell程序来执行子进程,当shell = True时,子进程将会由shell来执行,并且建议args参数使用String对象。
If args is a string, the string specifies the command to execute through the shell.
In [19]: p = subprocess.Popen("ipconfig /all",stdout = subprocess.PIPE,shell=True) #args = "ipconfig /all"
In [20]: pRet = p.stdout.read() #父进程获得执行结果
#On Windows with shell=True, the COMSPEC environment variable specifies the default shell.
#Win下相当于```args = ["cmd.exe","ipconfig","/all"]
On POSIX with shell=True, the shell defaults to /bin/sh.
Linux下相当于args = ["/bin/sh"," -c",args[0], args[1], ...]
bufsize
bufsize will be supplied as the corresponding argument to the open() function when creating the stdin/stdout/stderr pipe file objects.
0 means unbuffered (read and write are one system call and can return short)
1 means line buffered (only usable if universal_newlines=True i.e., in a text mode)
any other positive value means use a buffer of approximately that size
negative bufsize (the default) means the system default of io.DEFAULT_BUFFER_SIZE will be used.
当你将stdin/stdout/stderr重定向到PIPE或文件对象时,bufsize能够传递指定缓冲的方式给open()函数,并以此来创建文件对象:
0 表示无缓冲;
1 表示行缓冲;
otherNumber 表示缓冲区大小,
-1 是bufsize参数的缺省值表示使用系统缓冲(全缓冲)
注意:当子进程返回的数据达到缓存的Size时,子程序会等待付进程读取缓存数据。
close_fds
If close_fds is true, all file descriptors except 0, 1 and 2 will be closed before the child process is executed. (POSIX only). The default varies by platform: Always true on POSIX. On Windows it is true when stdin/stdout/stderr are None, false otherwise. On Windows, if close_fds is true then no handles will be inherited by the child process. Note that on Windows, you cannot set close_fds to true and also redirect the standard handles by setting stdin, stdout or stderr.
在Unix中,如果close_fds = True,除了0、1、2之外的文件描述符都会被关闭。如果在Windows中stdin/stdout/stderr = None,即继承父进程时,close_fds = True ,相反为False。在Windows下也不会继承其他文件描述符。
注意:在Windows中不能够在close_fds = True
的前提下对stdin, stdout or stderr做重定向(redirect ),锁定子进程的stdin/stdout/stderr。
In [24]: p = subprocess.Popen("ipconfig",shell=True,close_fds=True,stdout=subprocess.PIPE)
ValueError: close_fds is not supported on Windows platforms if you redirect stdin/stdout/stderr
其他参数含义
given, startupinfo:If given, startupinfo will be a STARTUPINFO object, which is passed to the underlying CreateProcess function. creationflags, if given, can be CREATE_NEW_CONSOLE or CREATE_NEW_PROCESS_GROUP. (Windows only)
在Windows中,调用CreateProcess()函数创建子进程时,startupinfo参数会传递STARTUPINFO对象,来设置子进程的外观等等属性。
executable:指定要执行的程序名,很少使用,一般用args参数来指定需要执行的子程序。可以指定执行子进程的shell(e.g. bash、csh、zsh)。Unix下,默认是/bin/sh。Windows下,就是环境变量%COMSPEC%的值。windows下,只有当你要执行的命令确实是shell内建命令(比如dir ,copy)时,你才需要指定shell=True
,而当你要执行一个基于命令行的批处理脚本的时候,不需要指定此项。
C:\Users\Username>echo %COMSPEC%
C:\WINDOWS\system32\cmd.exe
preexec_fn:If preexec_fn is set to a callable object, this object will be called in the child process just before the child is executed. (POSIX only)
钩子函数,只在Unix平台下有效,用于指定一个可执行对象(callable object),它将在子进程运行之前被调用。容易造成死锁,慎用。
cmd:指定了子进程的工作目录
注意:并不会把该目录做为可执行文件的搜索目录,所以不要把子程序文件所在目录设置为cwd 。
env:是一个字典类型,用于执行子进程的执行环节变量,而不使用默认继承父进程的环境变量
universal_newlines:为True时,子进程的stdout和stderr被视为文本对象,不管是Unix的行结束符’/n’,还是Mac格式的行结束符’/r’,还是Windows格式的行结束符’/r/n’都将被视为 ‘/n’处理 。
pass_fds:is an optional sequence of file descriptors to keep open between the parent and child. Providing any pass_fds forces close_fds to be True. (POSIX only)
restore_signals:If restore_signals is true (the default) all signals that Python has set to SIG_IGN are restored to SIG_DFL in the child process before the exec. Currently this includes the SIGPIPE, SIGXFZ and SIGXFSZ signals. (POSIX only)
start_new_session:If start_new_session is true the setsid() system call will be made in the child process prior to the execution of the subprocess. (POSIX only)
Popen成员函数
Popen.poll()
Check if child process has terminated. Set and return returncode attribute.
用于检查子进程是否已经结束。设置并返回returncode属性
Popen.wait(timeout=None)
Wait for child process to terminate. Set and return returncode attribute.
等待子进程结束。设置并返回returncode属性。使用Popen类创建的子进程时,父进程默认不会等待子进程结束,需要Call wait()函数来实现等待子进程结束后父进程继续执行。
timeout:If the process does not terminate after timeout seconds, raise a TimeoutExpired exception. It is safe to catch this exception and retry the wait.
注意: 如果子进程输出了大量数据到stdout或者stderr的管道,并达到了系统PIPE的缓存大小时,子进程会等待父进程读取管道内的数据,若此时父进程正在wait()的话,将不会读取管道内的数据,从而造成死锁,建议使用Pepon.communicate()来避免这种情况。
Popen.communicate(input=None,timeout=None)
与子进程进行交互。向stdin发送数据,可选参数input指定发送到子进程的数据。
Communicate()时从stdout和stderr中读取数据,直到文本末尾的EOF(进程之间采用文本通信),等待子进程结束。返回一个元组:(stdout_data, stderr_data),The data will be bytes or, if universal_newlines was True, strings.如果universal_newlines = True
则返回一个String。
注意:如果希望通过stdin向子进程发送数据,需要通过stdin=PIPE
创建管道对象。同样,如果希望从stdout和stderr获取数据,必须将stdout和stderr设置为PIPE。
p=subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
(stdout_data, stderr_data) = p.communicate()
Note:If the process does not terminate after timeout seconds, a TimeoutExpired exception will be raised. Catching this exception and retrying communication will not lose any output.
如果timeout还没有结束进程的话,需要捕捉触发的TimeoutExpired
异常,并且使用Popen.communicate()来保留子程序的输出。
proc = subprocess.Popen(...)
try:
outs, errs = proc.communicate(timeout=15)
except TimeoutExpired:
proc.kill()
outs, errs = proc.communicate()
Popen.send_signal(signal)
向子进程发送信号。
Popen.terminate()
停止子进程。在windows平台下,该方法将调用Windows API TerminateProcess()来结束子进程。
Popen.kill()
杀死子进程,在Windows上调用了TerminateProcess() API。在Unix上相当于发送了信号SIGTERM和SIGKILL。
Popen成员属性
Popen.pid
获取子进程的进程ID。
Popen.returncode
获取进程的返回值。如果进程还没有结束,返回None。
Popen.stdin
If the stdin argument was PIPE, this attribute is a writeable stream object as returned by open()
输入交互
#test.py
import sys
line = sys.stdin.readline()
print 'test',line
#run.py
from subprocess import *
p = Popen('./test.py',stdin=PIPE,stdout=PIPE)
p.stdin.write('say hi/n')
print p.stdout.readline()
Popen.stdout
If the stdout argument was PIPE, this attribute is a readable stream object as returned by open().
连续的输入输出交互
# test.py
import sys
while True:
line = sys.stdin.readline()
if not line:break
sys.stdout.write(line)
sys.stdout.flush()
# run.py
import sys
from subprocess import *
proc = Popen('./test.py',stdin=PIPE,stdout=PIPE,shell=True)
for line in sys.stdin:
proc.stdin.write(line) #子进程的输入
proc.stdin.flush()
output = proc.stdout.readline() #子进程的输出
sys.stdout.write(output) #父进程的打印
注意: run.py的flush和test.py中的flush,要记得清空缓冲区,否则程序得不到正确的输入和输出
Popen.stderr
If the stderr argument was PIPE, this attribute is a readable stream object as returned by open().
subprocess函数
subprocess函数本质上是对subprocess.Popen的封装,能够简便的创建子进程。当需要创建更加复杂的子进程时,建议使用Popen类,该类会生成子进程对象,且拥有多样化的成员方法和属性。
subprocess.call()
subprocess.call(args, *, stdin=None, stdout=None, stderr=None, shell=False, timeout=None)
父进程等待子进程完成
返回退出信息(returncode,相当于Linux exit code)
In [20]: subprocess.call("ipconfig")
.
.
Out[20]: 0
subprocess.check_call()
subprocess.check_call(args, *, stdin=None, stdout=None, stderr=None, shell=False, timeout=None)
父进程等待子进程完成
返回0
检查退出信息,如果returncode不为0,则举出错误subprocess.CalledProcessError,该对象包含有returncode属性,可用try…except…来检查
In [25]: subprocess.check_call("config",shell=True)
CalledProcessError: Command 'config' returned non-zero exit status 1
call()和check_call()的区别:两者的区别在于遇到错误的时候处理不一样,call()返回returncode ,check_call()再返回returncode后还会抛出异常。check_call实际上会调用call函数,然后加入了异常处理的情况。两者在本质上都会调用Popen().wait()来等待进程结束并返回returncode
subprocess.check_output()
subprocess.check_output(args, *, stdin=None, stderr=None, shell=False, universal_newlines=False, timeout=None)
父进程等待子进程完成
返回子进程向标准输出的输出结果
检查退出信息,如果returncode不为0,则举出错误subprocess.CalledProcessError,该对象包含有returncode属性和output属性,output属性为标准输出的输出结果,可用try…except…来检查。
最后
在使用subprocess模块的时候很容易卡死的情况出现,一些避免卡死的思路,我们之后再聊。 : -)
Python Module_subprocess_子进程(程序调用)的更多相关文章
- winfrom桌面程序调用python解释器
Winfrom桌面程序调用python解释器执行py脚本后台执行完成具体的功能,为什么要这样处理呢?因为我现在的大部分过项目都是后台的脚本处理,界面基本的输入完成之后,将参数按照规则传入到脚本的入口, ...
- python程序调用C/C++代码
这篇用来记录在些模拟Canoe生成CAN数据桢工具时遇到的问题, 生成CAN数据桢,主要分为两个关注点: 1.如何从can信号名获取到can信号的ID长度以及信号的起始位,并将信号值按照一定的规则填写 ...
- 快速掌握用python写并行程序
目录 一.大数据时代的现状 二.面对挑战的方法 2.1 并行计算 2.2 改用GPU处理计算密集型程序 3.3 分布式计算 三.用python写并行程序 3.1 进程与线程 3.2 全局解释器锁GIL ...
- 简明python教程 --C++程序员的视角(一):数值类型、字符串、运算符和控制流
最初的步骤 Python是大小写敏感的 任何在#符号右面的内容都是注释 >>> help('print')在“print”上使用引号,那样Python就可以理解我是希望获取关于“pr ...
- C程序调用shell脚本共有三种方法
C程序调用shell脚本共有三种法子 :system().popen().exec系列函数call_exec1.c ,内容为:system() 不用你自己去产生进程,它已经封装了,直接加入自己的命令e ...
- Python 的 JPype 模块调用 Jar 包
背景与需求 最近学习并安装使用了HttpRunner框架去尝试做接口测试,并有后续在公司推广的打算. HttpRunner由Python开发,调用接口时需要依赖Python:而大多数公司的扩展工具包使 ...
- Python装饰器的调用过程
在Python学习的过程中,装饰器是比较难理解的一个应用.本人也在学习期间也遇到很多坑,现将装饰器的基本调用过程总结一下. 首先,装饰器用到了“闭包”,而“闭包”是学习装饰器的基础,所以在讲装饰器之前 ...
- python下wxpython程序国际化的实践(中文英文切换)
一.什么是python的国际化(I18N) 有关I18N,百度上解释一大堆,个人比较喜欢这个说法. i18n是 Internationalization 这个英文的简写,因为International ...
- (转载)Python 的 JPype 模块调用 Jar 包
Python 的 JPype 模块调用 Jar 包 背景与需求 最近学习并安装使用了HttpRunner框架去尝试做接口测试,并有后续在公司推广的打算. HttpRunner由Python开发,调用接 ...
随机推荐
- git ssh 绑定 GitLab
入职新公司之后,需要使用GitLab,可是我不会啊,又不想麻烦运维大佬,所以自己找乐一下,发现网上都是些很陈旧的教程,所以准备自己记录下来 第一步 设置Git端上的用户名和用户邮箱: 假如入你已经安装 ...
- SOA架构简介
一. 什么是SOA 架构 SOA是一种架构模型,它可以根据需求通过网络对松散耦合的粗粒度应用组件进行分布式部署.组合和使用.服务层是SOA的基础,可以直接被应用调用,从而有效控制系统中与软件代理交互的 ...
- a标签前端下载火狐兼容和笔记
1.a标签实现前端下载的谷歌兼容 我们都知道,文件下载的一种实现方案就是后端返回文件流,然后前端进行生成a标签并触发点击来下载.但是在火狐浏览器的时候,需要注意一些兼容性问题.原因是火狐的同源策略.官 ...
- 【洛谷P3338】力
题目大意:求 \[ E_{j}=\sum_{i<j} \frac{q_{i}}{(i-j)^{2}}-\sum_{i>j} \frac{q_{i}}{(i-j)^{2}} \] 题解:可以 ...
- java课堂测试2(两种方式)
实验源代码 这是不使用数组形式的源代码 /* 2017/10/10 王翌淞 课堂测试2 */import java.util.Scanner; public class Number { public ...
- Puppet利用Nginx多端口实现负载均衡
随着公司应用需求的增加,需要不断的扩展,服务器数量也随之增加,当服务器数量不断增加,我们会发现一台puppetmaster压力大,解析缓慢,而且时不时出现"time out"之类的 ...
- windows窗口启动redis
必须先得配置好环境变量,才能在窗口开启 启动服务端:redis-server 启动客户端:redis-cli
- DB2的HADR
db2stop force; db2start; db2 restore db clmsdb; db2 start hadr on db clmsdb as standby; ************ ...
- js支持中文的hex编码 bin2hex (utf-8)
背景: 最近对接接口的时候需要将请求参数转为16进制,因此研究了下这个bin2hex.在js中转16进制 使用的是: str.charCodeAt(i).toString(16); 在遇到中文的时候编 ...
- [CF780C]Andryusha and Colored Balloons 题解
前言 完了,完了,咕值要没了,赶紧写题解QAQ. 题意简述 给相邻的三个节点颜色不能相同的树染色所需的最小颜色数. 题解 这道题目很显然可以用深搜. 考虑题目的限制,如果当前搜索到的点为u, 显然u的 ...