Python中subprocess模块的用法详解
作者:polyhedronx
Python subprocess模块
subprocess是用于启动进程,并与进程通信的模块。
格式
该模块定义了一个Popen类:
class Popen(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):
参数释义:
args
应该是字符串或程序参数序列,要执行的程序通常是args序列或字符串中的第一项,但可以使用
executable参数显式设置。
在UNIX 上,当 shell=False(默认), 类Popen 用 os.execvp() 来执行子程序,args通常应该是一个序列,如果args是一个字符串,它也会被视为只有一个元素的序列。
在UNIX 上,当 shell=True,如果 args 是字符串,它将作为命令行字符串通过shell 执行;如果是一个序列,它的第一项将作为命令行字符串,其他项将被视为附加的shell参数。
在 Windows 上,类Popen 用 CreateProcess() 来执行子程序,它以字符串作为参数。
如果args是一个序列,它将使用 list2cmdline 方法转换为字符串。需要注意的是,并非所有MS Windows应用程序都以相同的方式解释命令行,list2cmdline 是为使用与MS C运行规则相同的应用程序而设计的。
bufsize
如果被赋值,值将作为内建函数 open() 的参数,0意味着无缓冲,1就是行缓冲,任何其他正值意味着使用与该值大小接近的缓冲区。负bufsize意味着使用系统默认值,这通常意味着完全缓冲。 bufsize的默认值为0(无缓冲)。
stdin, stdout and stderr
分别代表子程序的标准输入,标准输出,标准错误输出的文件句柄。
有效值为PIPE、现有文件描述符(正整数)、现有文件对象和None。若赋值为PIPE ,就会为子程序创建新管道 ;若为None,不会发生重定向,子程序的文件句柄将从父程序继承。
另外,stderr可以是STDOUT,这表明子程序的错误数据可以被获得并发送到stdout输出。
preexec_fn 如果preexec_fn设置为可调用对象,则在执行子进程之前,将在子进程中调用此对象。
close_fds 若为true,则在执行子进程之前将关闭除0,1和2之外的所有文件描述符。
shell 若为true,则将通过shell执行指定的命令。
cwd 若不是None,在执行子进程之前,当前目录将更改为cwd。
env 若不是None,它将为新进程指定环境变量。
universal_newlines 文件对象stdout和stderr作为文本文件打开,但可以通过 '\n' (Unix), '\r' (Mac), '\r\n' (Win)断行。所有这些外部表示都被Python程序视为'\n'。注意:仅当Python使用通用换行支持(默认)构建时,此功能才可用。注意这些特征只在python支持通用换行的时候有效(默认支持)。
此外,communication() 方法不会更新文件对象stdout,stdin和stderr的换行属性。
startupinfo, creationflags 如果给定,将传递给底层的 CreateProcess() 函数。它可以指定主窗口的外观和新进程的优先级等内容。(仅限Windows)
该模块还定义了两个快捷功能:
call(*args, **kwargs):
使用参数运行命令。等待命令完成,然后返回returncode属性。
参数与Popen构造函数相同。例:
retcode = call(["ls", "-l"])
异常
在新程序开始执行之前,子进程中引发的异常将在父进程中重新引发。此外,异常对象将有一个名为'child_traceback'的额外属性,该属性是一个包含来自子进程视点的回溯信息的字符串。
引发的最常见异常是OSError。例如,在尝试执行不存在的文件时会发生这种情况。应用程序应当对OSErrors作出处理。
如果使用无效参数调用Popen,则会引发ValueError。
安全性
与其他一些 popen 函数不同,此实现永远不会隐式调用/bin/sh。
这意味着所有字符(包括shell元字符)都可以安全地传递给子进程。
Popen 对象
Popen类的实例具有以下方法:
Popen.poll():
检查子进程是否已终止。终止则返回returncode属性;否则,返回None。
Popen.wait(timeout=None):
等待子进程终止,返回returncode属性。
如果进程在超时秒后没有终止,则引发 TimeoutExpired 异常。捕获此异常并重试等待将会更安全。
注意:当使用stdout = PIPE或stderr = PIPE,并且子进程生成太多输出以致于阻止了等待OS管道缓冲区接受更多数据时,将导致死锁。使用管道时可以使用 Popen.communicate() 来避免这种情况。
注意:该功能使用忙循环(非阻塞呼叫和短暂睡眠)实现。
在Python3.3中添加了timeout参数。
从Python3.4开始不推荐使用endtime参数。这是无意中暴露在3.3中,但没有记录,因为它是私人内部使用。请改用timeout 。
Popen.communicate(input=None, timeout=None):
与进程交互:将数据发送到stdin。从stdout和stderr读取数据,直到达到文件结尾。等待进程终止。可选的input参数应该是要发送到子进程的数据,如果没有数据需要发送给子进程,则设为None。如果在文本(text)模式下打开流,则input参数必须是字符串(string);否则,它必须是字节(bytes)。
communic()返回一个 tuple(stdout_data, stderr_data) 。如果在文本模式下打开流,则数据将是字符串;否则,是字节。
注意:如果要将数据发送到进程的stdin,则需要使用stdin = PIPE创建Popen对象。同样,要在结果元组(tuple)中获取除None之外的任何内容,还需要设置stdout = PIPE and/or stderr = PIPE。
如果进程在超时(timeout)秒后没有终止,则会引发TimeoutExpired异常。捕获此异常并重试通信不会丢失任何输出。
如果超时到期,则子进程不会被终止,因此为了正确清理,行为良好的应用程序应该终止子进程并完成通信:
proc = subprocess.Popen(...) try: outs, errs = proc.communicate(timeout=15) except TimeoutExpired: proc.kill() outs, errs = proc.communicate()
注意:读取的数据缓冲在内存中,因此如果数据大小很大或不受限制,请不要使用此方法。
在Python3.3中添加了timeout参数。
Popen.send_signal(signal):
向子进程发送signal信号。
注意:在 Windows 上,SIGTERM和terminate() 的作用相同。可以将 CTRL_C_EVENT 和 CTRL_BREAK_EVENT 发送到使用 creationflags 参数启动的进程,该参数包括 CREATE_NEW_PROCESS_GROUP 。
Popen.terminate():
终止(stop)子进程。在Posix OSs上,该方法将SIGTERM发送给子进程。在Windows上,调用Win32 API函数TerminateProcess() 来停止子进程。
Popen.kill():
杀死子进程。在Posix OSs上,该方法将 SIGKILL 发送给子进程。在Windows上,kill() 和terminate() 的作用相同。
还提供以下属性:
Popen.args:
args参数传递给Popen 程序参数序列或单个字符串。
Python3.3中的新功能。
Popen.stdin:
如果stdin参数是PIPE,则此属性是open() 返回的可写流对象。如果指定了encoding或errors参数或者universal_newlines参数为True,则流是文本流,否则它是字节流。如果stdin参数不是PIPE,则此属性为None。
obj.stdin.write(" args ")
Popen.stdout:
如果stdout参数是PIPE,则此属性是open() 返回的可读流对象。从流中读取提供子进程的输出。如果指定了encoding或errors参数或者universal_newlines参数为True,则流是文本流,否则它是字节流。如果stdout参数不是PIPE,则此属性为None。
obj.stdout.read()
Popen.stderr:
如果stderr参数是PIPE,则此属性是open() 返回的可读流对象。从流中读取提供子进程的错误输出。如果指定了encoding或errors参数或者universal_newlines参数为True,则流是文本流,否则它是字节流。如果stderr参数不是PIPE,则此属性为None。
警告:使用 communic() 而不是 .stdin.write,.stdout.read 或 .stderr.read 来避免由于任何其他OS管道缓冲区填满和阻止子进程而导致的死锁。
Popen.pid:
子进程的进程ID。
注意:如果将shell参数设置为True,则这是生成的shell的进程ID。
Popen.returncode:
子进程返回码,由poll() 和wait() 设置(间接通过communic())。 None 表示该进程尚未终止,负值 N 表示子进程被信号 N 终止(仅限POSIX)。
- returncode=0 表示执行成功
- returncode=127 表示语句为空串
- returncode=17 表示找不到表
- returncode=64 表示缺失关键字
- returncode=41 表示查询的字段不存在
例如:
import os import shlex import signal import subprocess shell_cmd = 'ping www.baidu.com' cmd = shlex.split(shell_cmd) p = subprocess.Popen(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) # ***CTRL_C信号*** # os.kill(p.pid, signal.CTRL_C_EVENT) # p.send_signal(signal.CTRL_C_EVENT) # ***SIGTERM信号*** # p.send_signal(signal.SIGTERM) # p.terminate() # p.kill() while p.poll() is None: # 当子进程未终止 line = p.stdout.readline().decode('gbk').strip() print(line) if p.returncode == 0: print('Subprogram success') else: print('Subprogram failed')
用 subprocess 模块替换旧函数
在本节中,“a ==> b”表示a可以被b替代。
注意:如果找不到执行的程序,则本节中的所有函数都会或多或少的调用失败。此模块引发OSError异常。
在以下示例中,我们假设 subprocess 模块使用 from subprocess import * 导入。
替换/bin/sh shell反引号
output=`mycmd myarg` ==> output = Popen(["mycmd", "myarg"], stdout=PIPE).communicate()[0]
替换shell管道
output=`dmesg | grep hda` ==> p1 = Popen(["dmesg"], stdout=PIPE) p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE) output = p2.communicate()[0]
替换os.system()
sts = os.system("mycmd" + " myarg") ==> p = Popen("mycmd" + " myarg", shell=True) sts = os.waitpid(p.pid, 0)
注意:
- 通常不需要通过shell调用程序。
- 查看returncode属性比退出状态更容易。
一个更实际的例子:
try: retcode = call("mycmd" + " myarg", shell=True) if retcode < 0: print >>sys.stderr, "Child was terminated by signal", -retcode else: print >>sys.stderr, "Child returned", retcode except OSError, e: print >>sys.stderr, "Execution failed:", e
替换os.spawn*
P_NOWAIT示例:
pid = os.spawnlp(os.P_NOWAIT, "/bin/mycmd", "mycmd", "myarg") ==> pid = Popen(["/bin/mycmd", "myarg"]).pid
P_WAIT示例:
retcode = os.spawnlp(os.P_WAIT, "/bin/mycmd", "mycmd", "myarg") ==> retcode = call(["/bin/mycmd", "myarg"])
Vector示例:
os.spawnvp(os.P_NOWAIT, path, args) ==> Popen([path] + args[1:])
Environment示例:
os.spawnlpe(os.P_NOWAIT, "/bin/mycmd", "mycmd", "myarg", env) ==> Popen(["/bin/mycmd", "myarg"], env={"PATH": "/usr/bin"})
替换os.popen*
pipe = os.popen(cmd, mode='r', bufsize) ==> pipe = Popen(cmd, shell=True, bufsize=bufsize, stdout=PIPE).stdout pipe = os.popen(cmd, mode='w', bufsize) ==> pipe = Popen(cmd, shell=True, bufsize=bufsize, stdin=PIPE).stdin (child_stdin, child_stdout) = os.popen2(cmd, mode, bufsize) ==> p = Popen(cmd, shell=True, bufsize=bufsize, stdin=PIPE, stdout=PIPE, close_fds=True) (child_stdin, child_stdout) = (p.stdin, p.stdout) (child_stdin, child_stdout, child_stderr) = os.popen3(cmd, mode, bufsize) ==> p = Popen(cmd, shell=True, bufsize=bufsize, stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True) (child_stdin, child_stdout, child_stderr) = (p.stdin, p.stdout, p.stderr) (child_stdin, child_stdout_and_stderr) = os.popen4(cmd, mode, bufsize) ==> p = Popen(cmd, shell=True, bufsize=bufsize, stdin=PIPE, stdout=PIPE, stderr=STDOUT, close_fds=True) (child_stdin, child_stdout_and_stderr) = (p.stdin, p.stdout)
替换popen2.*
注意:如果popen2函数的cmd参数是一个字符串,则该命令通过 /bin/sh 执行。如果是列表,则直接执行该命令。
(child_stdout, child_stdin) = popen2.popen2("somestring", bufsize, mode) ==> p = Popen(["somestring"], shell=True, bufsize=bufsize stdin=PIPE, stdout=PIPE, close_fds=True) (child_stdout, child_stdin) = (p.stdout, p.stdin) (child_stdout, child_stdin) = popen2.popen2(["mycmd", "myarg"], bufsize, mode) ==> p = Popen(["mycmd", "myarg"], bufsize=bufsize, stdin=PIPE, stdout=PIPE, close_fds=True) (child_stdout, child_stdin) = (p.stdout, p.stdin)
popen2.Popen3 和 popen3.Popen4 基本上用作subprocess.Popen,除了:
subprocess.Popen如果执行失败则引发异常。
capturestderr参数被替换为stderr参数。
必须指定stdin = PIPE和stdout = PIPE。
popen2默认关闭所有文件描述符,但必须使用subprocess.Popen指定close_fds = True。
到此这篇关于Python中subprocess模块的用法详解的文章就介绍到这了,更多相关Python中subprocess模块内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!