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)
subprocess.startupinfo 详解网址:
http://www.programcreek.com/python/example/5376/subprocess.STARTUPINFO
该模块还定义了两个快捷功能:
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() 来避免这种情况。
注意:该功能使用忙循环(非阻塞呼叫和短暂睡眠)实现。使用asyncio模块进行异步等待:请参阅asyncio.create_subprocess_exec。
在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.Popen()的简单用法
17.5. subprocess — Subprocess management
PEP 324 -- subprocess - New process module
python中使用subprocess.Popen中的返回值总结
Python_cmd的各种实现方法及优劣(subprocess.Popen, os.system和commands.getstatusoutput)