最近写了个脚本,需要执行第三方程序,并根据程序输出判断执行情况,当程序执行时间过长时kill该进程。因此选用了subprocess模块。
process = subprocess.Popen(self.cmd,stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE, shell=True ,close_fds=True)
1. 在linux下,不加参数 shell=True 会报错
2. 当没有参数 close_fds=True 时,打开的标准输入、标准输出、标准错误不会被关闭,当打开的文件数量超过最大值时会产生“OSError: [Errno 24] Too many open files”异常。(但也有人说不起作用需要自己关闭标准输入、标准输出、标准错误,但是暂时没有发现这种情况)
3. 当不使用process.terminate()终止进程后,再使用process.kill(),会导致进程kill失败
4. 开始使用process.stdout.readline()在子线程中循环读取管道内容,虽然中间有sleep但也十分消耗资源。(后改为进程结束时使用process.stdout.read()读取全部内容)
记录下来防止时间长了,忘记了。不对的地方还请指正。。。
#coding=utf-8
'''
Created on 2015-8-13
@author: xhw
@explain: pass(添加返回索引输出内容:给合并索引模块使用,判断是否合并成功)
'''
import time
import subprocess
import threading
import common.logger as log
from common.traceback_error import get_err_msg
from common.MailHandler import SendMail
import sys,os,shutil
system = sys.platform
Maxtime = 999999
reload(sys)
sys.setdefaultencoding('utf-8')
class CmdServer:
def __init__(self,cmd,timeout,taskname):
'''
@cmd:命令字符串
@timeout:任务超时时间(进程运行超过该时间,kill该进程)
@taskname:任务名称(根据该任务名称记录命令输出信息)
'''
print cmd,timeout,taskname,time.strftime("%Y-%m-%d %H:%M:%S")
self.cmd = cmd
if timeout:
self.timeout = int(timeout)
else:
self.timeout = Maxtime
self.path = os.path.join(os.path.dirname(__file__),'cmd_stdout')
self.stdoutfile = os.path.join(self.path,taskname + '.txt')
self.renamefile = os.path.join(self.path,taskname + '1.txt')
def run(self):
self.writeStdout(self.cmd+'\n')
try:
#在windows下运行非shell命令时,使用shell=True,不能使子进程结束(linux下没有测试)
process = subprocess.Popen(self.cmd,stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE, shell=True ,close_fds=True)
num = 0
while process.poll() == None:
if num >= self.timeout:
self.writeStdout(u"任务超时!\n")
try:
process.terminate()
stdout = self.readStdout(process)
process.kill()
process.wait()
self.writeStdout(u"任务kill!\n")
return False,"Task running timeout!"+str(stdout)
except:
errmsg = get_err_msg()
log.CmdRun_Logger.error(errmsg)
self.writeStdout(u"任务kill失败!\n")
SendMail(subject = 'command_worker', content = errmsg)
return False,"Task running timeout,Kill failure"+str(stdout)
time.sleep(1)
num+= 1
if process.poll() is not None:
self.writeStdout(u"任务结束!\n")
stdout = self.readStdout(process)
process.wait()
return True,stdout
except:
errmsg = get_err_msg()
log.CmdRun_Logger.error(errmsg)
SendMail(subject = 'command_worker', content = errmsg)
return False,errmsg
def readStdout(self,process):
''' 读取执行命令进程输出的内容,并写入日志文件'''
stdout = ""
try:
stdout = process.stdout.read()
if stdout:
if 'win' in system:
stdout = stdout.decode('gbk','ignore').encode('utf-8')
self.writeStdout(stdout)
except:
errmsg = get_err_msg()
log.CmdRun_Logger.error(errmsg)
SendMail(subject = 'command_worker', content = errmsg)
return stdout
def writeStdout(self,stdout):
''' 记录命令输出'''
try:
stdout = stdout.strip()
with open(self.stdoutfile, 'a+') as f:
f.write(stdout+'\r\n')
except:
errmsg = get_err_msg()
log.CmdRun_Logger.error(errmsg)
SendMail(subject = 'command_worker', content = errmsg)
self.checklogfile()
def checklogfile(self):
''' 这里使用日志模块并不清楚会有那些任务,所以没有使用配置文件,但不使用配置文件,会在日志文件设置上出现问题,
在这里自己替换超过大小到日志文件(默认两个日志文件,每个10M)'''
if not os.path.exists(self.stdoutfile):
return
if os.path.getsize(self.stdoutfile) > 1024*1024*10:
try:
print self.stdoutfile, self.renamefile
#os.rename(self.stdoutfile, self.renamefile)#会提示WindowsError: [Error 32],不清楚是不是OS模块操作文件没有关闭引起的
shutil.copyfile(self.stdoutfile, self.renamefile)
with open(self.stdoutfile,'w') as f:
f.write(time.strftime('%Y-%m-%d %H:%M:%S'+'\r\n'))
except:
errmsg = get_err_msg()
log.CmdRun_Logger.error(errmsg)
SendMail(subject = 'command_worker', content = errmsg)
if __name__ == '__main__':
A = CmdServer(r'python E:\work\sphinx_task\test_task\test3.py',50,'test3')
A.run()