【Python】 子进程创建与使用subprocess
subprocess
*****本文参考了Vamei大神的http://www.cnblogs.com/vamei/archive/2012/09/23/2698014.html
运用subprocess包可以在运行python的进程下进一步开启一个子进程,创建子进程要注意
1. 父进程是否暂停
2.创建出的子进程返回了什么
3.执行出错,即返回的code不是0的时候应该如何处理
subprocess包提供了三个开启子进程的方法,subprocess.call() , subprocess.check_call() , subprocess.check_output(),给三者传递命令字符串作为参数。可以用(['ping','www.baidu.com','-c','3'])这种列表的形式,同时也可以是("ping www.baidu.com -c 3") 这种形式。在开启子进程的时候,可以加上shell=True的参数来让python开启一个shell,通过shell来解释获得的命令。一般在windows下运行的程序最好都把shell=True加上,这样才能顺利地执行dos命令,但是linux下似乎不加也没啥关系。因为linux下未指明用shell执行的话会调用/bin/sh来执行,问题不大,但是dos下系统不会默认用cmd.exe来执行命令,所以要加上shell=True。
subprocess.call ; subprocess.check_call ; subprocess.check_output 这三者的区别在于,返回的值分别是,子进程的执行返回码;若返回码是0则返回0,否则出错的话raise起CalledProcessError,可以用except处理之;若返回码是0则返回子进程向stdout输出的结果,否则也raise起CalledProcessError。另外,这三个方法都是让父进程挂起等待的,在子进程结束之前,父进程不会继续往下运行。
另外从本质上讲,上述三个方法都是对subprocess.Popen方法的一个包装,Popen开启的子进程是不会让父进程等待其完成的,除非调用了wait()方法:
child = subprocess.Popen("...",shell=True)
print "Hello"
"""
很可能hello在子进程的输出之前就被打印出来了,因为父进程不等child子进程运行完
"""
child = subprocess.Popen("...",shell=True)
child.wait()
print "Hello"
"""
这就不一样,父进程一定会等子进程运行完,给出完整的结果之后再继续往下执行。相当于wait函数挂起了父进程。
"""
此外,上面代码里的child这个对象还有其他的一些方法:
child.poll() 返回子进程运行状态,主要是两种结果,None代表尚未运行完,而一个返回码则代表已经运行完成并且是成功或失败了
child.kill() 强行终止子进程
child.send_signal(...) 向子进程发送一个信号(具体信号是以什么方式表示不清楚,还待研究)
child.terminate() 终止子进程
child.pid 子进程的pid
child.returncode 子进程的返回码
child.stdin/stdout/stderr 子进程的标准输入流,标准输出和标准错误输出,都是类文件对象
■ 文本流控制
每个子进程对象都有stdin/stdout/stderr三个对象,而在Popen开启子进程的时候,可以设置这三个对象。比如
child1 = subprocess.Popen("cmd1",shell=True, stdout=subprocess.PIPE)
'''
child1的stdout被设置成管道,可以把它理解成一个第三方托管机构,
因为不设置的话child1的stdout的内容就直接被打印到父进程的stdout里了,
设置成管道之后内容被导入了PIPEという名の第三方托管机构里
'''
child2 = subprocess.Popen("cmd2",shell=True,stdin=child1.stdout,stdout=subprocess.PIPE)
'''
把child2的stdin设置成了child1的stdout,也就是之前那个第三方机构,这么一来就实现了两个子进程之间的数据通信了。
而把child2的stdout也设置成第三方,是因为不想让child2的输出就直接这么输出到父进程的stdout里,而要对它做一些处理
'''
stdout,tmp = child2.communicate()
'''
因为child2的输出不用再转给个child3去处理,就用communicate方法把第三方机构那里的数据取出来放进一个变量里。
注意,这里的stdout已经是个str对象了,communicate出来的都是字符串了
communicate方法自带wait功能,会让父进程挂起等待所有子进程结束
communicate会返回一个元组,但是像在这个例子中没有设置stderr=PIPE,所以元组中的第二项原本属于stderr的值的地方的tmp的值是None,如果设置了其为PIPE,由于没有错误信息tmp是""。这点是有区别的。
'''
print "We have result:\n%s"%(stdout) #代表了把stdout做一些处理后再输出
■ Popen方法
Popen就是开启一个新的子进程,常用的几个参数正如上面所提的cmd,shell,stdin,stdout,stderr来指定开启的子进程的一些属性。
除此之外还有以下的参数:
close_fds 默认为False,设置为True的情况下会在子进程执行之前关闭所有除了0,1,2之外的所有文件都关闭(虽然不知道有什么意义)
cwd 默认None,可以为子进程设置工作目录
盗了张图(http://www.cnblogs.com/zhoug2020/p/5079407.html)

实际上,我一般都是这么干的:
import subprocess
p = subprocess.Popen("CMD",shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
stdout,stderr = p.communicate()
if stderr != "":
print "ERROR:"+stderr
else:
print "RESULT:"+stdout
*这篇文章(http://www.tuicool.com/articles/bUNJ7v)提供了一个解决大量并发开启子进程时出bug的方案,同时也给了一个如何为一个子进程设置超时的方法,值得看一下:
def timeout_command(command, timeout):
start = datetime.datetime.now()
process = subprocess.Popen(command, bufsize=10000, stdout=subprocess.PIPE, close_fds=True)
'''
这个循环就是为子进程设置了超时功能,感觉还挺巧妙的。。
'''
while process.poll() is None:
time.sleep(0.1)
now = datetime.datetime.now()
if (now - start).seconds> timeout:
try:
process.terminate()
except Exception,e:
return None
return None
out = process.communicate()[0]
if process.stdin:
process.stdin.close()
if process.stdout:
process.stdout.close()
if process.stderr:
process.stderr.close()
try:
process.kill()
except OSError:
pass
return out
■ 关于实时获取子进程输出的方法
之前用过的所有subprocess.Popen方法吧,打开的子进程都比较短小,其命令基本上都可以在一秒内完成。所以在communicate的时候都没有显示出什么不妥的地方。但是碰到一些比较大,运行时间比较长的命令时,communicate就显得有些不太好了,因为到命令运行完成或者缓冲区满为止,子进程对象是不会向程序返回输出内容的。此时就需要变通一点不要使用communicate了。
一个解决的办法是这样的:
import subprocess
import sys p = subprocess.Popen('cmd',shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE) while p.poll() is None: #当子进程没有完成之前
char = p.stdout.read(1)
sys.stdout.write(char) stdout,stderr = p.communicate()
sys.stdout.write(stdout)
观察这段程序,可以看到,当子进程没有结束之前,循环将不断地从子进程的stdout中读取一个字符的数据然后写到父进程的stdout中。如果子进程较长时间没有明文进入stdout的话也可以在循环中加上一个time.sleep来控制循环频率。下面再加上communicate是为了保证信息输出的完整性。因为当子进程结束之后,有可能stdout还没有读取光,如果不加communicate的话那么还剩余在stdout中的信息就丢失了。如果子进程在stderr中有输出那么也可以放在communicate后面判断。另外也可以在建立p对象的时候把stderr参宿设置为subprocess.STDOUT来把子进程的stderr输出重定向到stdout中。
*测试的时候,把一个每sleep1秒就向stdout写入一些信息的脚本当做子进程。但是发现以上方法并不奏效。想了下之后,记起来python在写文件的时候是会有缓冲区这个设定的。也就是说,子进程代码中的write被调用后stdout并不马上把信息输出到stdout中。解决办法就是用file对象(这里是sys.stdout)调用flush()方法来清空缓存并写入文件。
【Python】 子进程创建与使用subprocess的更多相关文章
- python子进程模块subprocess详解与应用实例 之三
二.应用实例解析 2.1 subprocess模块的使用 1. subprocess.call >>> subprocess.call(["ls", " ...
- Python子进程 (subprocess包)
Python子进程 (subprocess包) subprocess以及常用的封装函数 当我们运行python的时候,我们都是在创建并运行一个进程.正如我们在Linux进程基础中介绍的那样,一个进程可 ...
- 【转】Python之系统交互(subprocess)
[转]Python之系统交互(subprocess) 本节内容 os与commands模块 subprocess模块 subprocess.Popen类 总结 我们几乎可以在任何操作系统上通过命令行指 ...
- Python 调用系统命令的模块 Subprocess
Python 调用系统命令的模块 Subprocess 有些时候需要调用系统内部的一些命令,或者给某个应用命令传不定参数时可以使用该模块. 初识 Subprocess 模块 Subprocess 模块 ...
- Python学习---重点模块之subprocess
subprocess是用来执行系统程序,查看系统的模块, 查看当前目录 第一种方法: import subprocess # subprocess会单独自己开辟一个线程,内部是多线程 # stdout ...
- Python 动态创建函数【转】
知乎上也有相似的问题 偶然碰到一个问题,初想是通过动态创建Python函数的方式来解决,于是调研了动态创建Python函数的方法. 定义lambda函数 在Python中定义lambda函数的写法很简 ...
- linux进程编程:子进程创建及执行函数简介
linux进程编程:子进程创建及执行函数简介 子进程创建及执行函数有三个: (1)fork();(2)exec();(3)system(); 下面分别做详细介绍.(1)fork() 函数定 ...
- Python之创建tuple和“可变”的tuple
Python之创建tuple tuple是另一种有序的列表,中文翻译为" 元组 ".tuple 和 list 非常类似,但是,tuple一旦创建完毕,就不能修改了. 同样是表示班里 ...
- cocos2d-x使用python脚本创建项目的简单方法
本文有CC原创,转载请注明地址:http://blog.csdn.net/oktears/article/details/13297003 在cocos2d-x2.1.4以上的版本中,取消了使用vs模 ...
随机推荐
- JUnit4测试出错(一)
log4j:WARN No appenders could be found for logger (org.springframework.test.context.junit4.SpringJUn ...
- javascript学习笔记01--javascript的基本介绍
javascript 的基本介绍1,是用于web开发的脚本语言①脚本语言往往不能独立使用 它需要和html等配合使用②脚本语言有自己的变量,函数 控制语句③解释性语言/编译语言 脚本语言实际是解释性语 ...
- Flex中的FusionCharts 2D柱形图
1.2D柱形图源码 <?xml version="1.0" encoding="utf-8"?> <s:Application xmlns:f ...
- PL/SQL 记录 Record 简介
记录类型是表中当行数据结构的一个镜像.每个记录只存储一行数据,记录包含的是字段,而不是列. 1.用%rowtype属性隐式定义记录类型 declare individual individuals%r ...
- ubuntu14.04 64位 安装eclipse出错
1 错误描述 org.eclipse.m2e.logback.configuration: The org.eclipse.m2e.logback.configuration bundle was a ...
- Struts2(三) 配置struts.xml的提示(在不联网的情况下)
开发过程中如果可以上网,struts.xml 会自动缓存dtd,提供提示功能.如果不能联网需要我们配置本地dtd,这样才能让struts2 产生提示 1.首先,在EClipse中依次点击工具栏中的wi ...
- iOS - Quartz 2D 第三方框架 Charts 绘制图表
1.Charts 简介 使用第三方框架 Charts 绘制 iOS 图表.GitHub 源码 Charts Charts 是一款用于绘制图表的框架,可以绘制柱状图.折线图.K线图.饼状图等.Chart ...
- Hibernate【与Spring整合】
前言 前面已经学习了如何使用Spring与Struts2进行整合,本博文主要讲解如何使用Spring对Hibernate进行整合 Spring和Hibernate整合的关键点: SessionFact ...
- JavaScript之BOM
一.什么是BOM? BOM(Browser Object Model)是指浏览器对象模型,它使 JavaScript 有能力与浏览器进行“对话”. 二.Windows对象 Window对象是客户端Ja ...
- 关于Flex的DataGridColumn 的editorDataField属性
本人Flex菜鸟,纯纯的小白 最近在项目中提了一个需求,其实这个需求就是在原有的列表中接一列数据,只不过这一列数据是下拉列表选择的 最关键的是貌似以前的某一位大神已经添加过,我的工作就是调通,可是就是 ...