使用subprocess模块,本模块为开辟子进程去执行子程序提供了统一的接口

try:
    process = subprocess.Popen(cmd,stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
    output, unused_err = process.communicate(timeout=30)
    output = output.decode("utf-8")
    print(output)
except Exception as e:
    process.kill()
    print(e, e.__traceback__.tb_lineno, '行')

1.3 问题

出现一个问题就是日志并不是实时输出,需要将日志实时打印输出并且设置执行超时时间

1.4 问题修复

def run_cmd(cmd_string, timeout=300):
    Logger.logger.info("命令为:" + cmd_string)
    start = datetime.datetime.now()
    p = subprocess.Popen(cmd_string, stderr=subprocess.STDOUT, stdout=subprocess.PIPE, shell=True, close_fds=True,
                         start_new_session=True)
    formats = 'gbk' if platform.system() == "Windows" else 'utf-8'
    time_flag = True
    try:
        while p.poll() is None and time_flag:
            line = p.stdout.readline()
            line = line.strip()
            if line:
                Logger.logger.info(line.decode(formats))
                time.sleep(0.01)
            now = datetime.datetime.now()
            if (now - start).seconds > timeout:
                time_flag = False
        # (msg, errs) = p.communicate(timeout=timeout)
        if p.returncode:
            code = 4
            msg = "[ERROR]执行异常"
        else:
            code = 2
            msg = "[INFO]执行成功"
    except subprocess.TimeoutExpired:
        # 注意:不能只使用p.kill和p.terminate,无法杀干净所有的子进程,需要使用os.killpg
        p.kill()
        p.terminate()
        os.killpg(p.pid, signal.SIGTERM)
        # 如果开启下面这两行的话,会等到执行完成才报超时错误,但是可以输出执行结果
        # (outs, errs) = p.communicate()
        # print(outs.decode('utf-8'))
        code = 4
        msg = "[ERROR]Timeout Error : Command '" + cmd_string + "' timed out after " + str(timeout) + " seconds"
    except Exception as e:
        code = 4
        msg = "[ERROR]Unknown Error : " + str(e)
    return code

通过持续循环输出子进程打印的日志信息

二、subprocess使用

subprocess模块用来生成子进程,并可以通过管道连接它们的输入/输出/错误,以及获得它们的返回值

2.1 使用subprocess模块

2.1.1 subprocess.call
subprocess.call(args, *, stdin=None, stdout=None, stderr=None, shell=False)

运行由args指定的命令,直到命令结束后,返回 返回码的属性值

import subprocess
out = subprocess.call("ls -l", shell=True)
out = subprocess.call("cd ..", shell=True)
2.1.2 subprocess.check_call
subprocess.check_call(args, *, stdin=None, stdout=None, stderr=None, shell=False)
  • 语义

    运行由args指定的命令,直到命令执行完成
    如果返回码为零,则返回。否则,抛出 CalledProcessError异常
    CalledProcessError对象包含有返回码的属性值
    示例

subprocess.check_call(["ls", "-l"])
subprocess.check_call("exit 1", shell=True)
Traceback (most recent call last):
   ...
subprocess.CalledProcessError: Command 'exit 1' returned non-zero exit status 1
2.1.3 subprocess.check_output
subprocess.check_output(args, *, stdin=None, stderr=None, shell=False, universal_newlines=False)
  • 语义

    运行args定义的命令,并返回一个字符串表示的输出值
    如果返回码为非零,则抛出 CalledProcessError异常

subprocess.check_output(["echo", "Hello World!"])
'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

2.2 Popen类

subprocess中底层的进程创建和管理可以通过Popen类实现,它提供了更多的灵活性,通过它能处理更多复杂的情况

subprocess模块中定义了一个Popen类,通过它可以来创建进程,并与其进行复杂的交互

2.2.1 构造函数
__init__(
		self, 
		args, 
		bufsize=0, 
		executable=None, 
		stdin=None, 
		stdout=None, 
		stderr=None, 
		preexec_fn=None, 
		close_fds=False, 
		shell=False, 
		cwd=None, 
		env=None, 
		universal_newlines=False, 
		startupinfo=None, 
		creationflags=0
2.2.2 主要参数
  • args
    should be a string, or a sequence of program arguments,也就是说必须是一个字符串或者序列类型(如:字符串、list、元组),用于指定进程的可执行文件及其参数
    如果是一个序列类型参数,则序列的第一个元素通常都必须是一个可执行文件的路径。当然也可以使用executeable参数来指定可执行文件的路径

  • stdin/stdout/stderr
    分别表示程序的标准输入、标准输出、标准错误
    有效的值可以是PIPE,存在的文件描述符,存在的文件对象或None,如果为None需从父进程继承过来,stdout可以是PIPE,表示对子进程创建一个管道,stderr可以是STDOUT,表示标准错误数据应该从应用程序中捕获并作为标准输出流stdout的文件句柄

  • shell
    如果这个参数被设置为True,程序将通过shell来执行

  • env
    描述的是子进程的环境变量,如果为None,子进程的环境变量将从父进程继承而来

2.2.3 Popen类的实例
res = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
  • cmd
    标准像子进程传入需要执行的shell命令,如:ls -al

  • subprocess.PIPE
    在创建Popen对象时,subprocess.PIPE可以初始化为stdin, stdoutstderr的参数,表示与子进程通信的标准输入流,标准输出流以及标准错误

  • subprocess.STDOUT
    作为Popen对象的stderr的参数,表示将标准错误通过标准输出流输出

2.2.4 Popen类方法及属性
  • Popen.pid

获取子进程的进程ID

  • Popen.returncode

获取进程的返回码。如果进程未结束,将返回None

  • communicate(input=None)

与子进程进行交互,像stdin发送数据,并从stdout和stderr读出数据存在一个tuple中并返回
参数input应该是一个发送给子进程的字符串,如果未指定数据,将传入None

  • poll()

检查子进程是否结束,并返回returncode属性

  • wait()

Wait for child process to terminate. Returns returncode attribute
等待子进程执行结束,并返回returncode属性,如果为0表示执行成功

  • send_signal( sig)

Send a signal to the process
发送信号给子进程

  • terminate()

Terminates the process
终止子进程

windows下将调用Windows API TerminateProcess()来结束子进程

  • kill()

跟terminate()是一样的,表示杀死子进程

import time   tt = '555'   cmd = "python /home/100003/python/mypython/sub2.py "+" 333"+" 444 "+tt   print time.time()   process = subprocess.Popen(cmd,stdin=subprocess.PIPE, stdout=subprocess.PIPE) output, unused_err = process.communicate(timeout=30) output = output.decode("utf-8") print(output)