17.5. popen2 — 具有可访问 I/O 流的子进程 — Python 文档
17.5. popen2 — 具有可访问 I/O 流的子进程
该模块允许您在 Unix 和 Windows 下生成进程并连接到它们的输入/输出/错误管道并获取它们的返回代码。
subprocess 模块为生成新进程和检索其结果提供了更强大的工具。 使用 subprocess 模块比使用 popen2 模块更可取。
该模块提供的主要接口是三个工厂函数。 对于其中的每一个,如果指定了 bufsize,则它指定了 I/O 管道的缓冲区大小。 mode,如果提供,应该是字符串 'b'
或 't'
; 在 Windows 上,这需要确定文件对象应该以二进制还是文本模式打开。 mode 的默认值为 't'
。
在 Unix 上,cmd 可能是一个序列,在这种情况下,参数将直接传递给程序而无需 shell 干预(如 os.spawnv())。 如果 cmd 是一个字符串,它将被传递给 shell(与 os.system() 一样)。
检索子进程返回码的唯一方法是使用 Popen3 和 Popen4 类上的 poll()
或 wait()
方法; 这些仅在 Unix 上可用。 使用 popen2()、popen3() 和 popen4() 函数或 os 中的等效函数时,此信息不可用 模块。 (请注意,os 模块函数返回的元组与 popen2 模块返回的元组顺序不同。)
- popen2.popen2(cmd[, bufsize[, mode]])
- 执行 cmd 作为子进程。 返回文件对象
(child_stdout, child_stdin)
。
- popen2.popen3(cmd[, bufsize[, mode]])
- 执行 cmd 作为子进程。 返回文件对象
(child_stdout, child_stdin, child_stderr)
。
- popen2.popen4(cmd[, bufsize[, mode]])
执行 cmd 作为子进程。 返回文件对象
(child_stdout_and_stderr, child_stdin)
。2.0 版中的新功能。
在 Unix 上,还可以使用定义工厂函数返回的对象的类。 这些不用于 Windows 实现,并且在该平台上不可用。
- class popen2.Popen3(cmd[, capturestderr[, bufsize]])
这个类代表一个子进程。 通常,Popen3 实例是使用上述 popen2() 和 popen3() 工厂函数创建的。
如果不使用帮助函数之一来创建 Popen3 对象,则参数 cmd 是要在子进程中执行的 shell 命令。 capturestderr 标志(如果为真)指定对象应捕获子进程的标准错误输出。 默认值为假。 如果指定了 bufsize 参数,则它指定进出子进程的 I/O 缓冲区的大小。
- class popen2.Popen4(cmd[, bufsize])
类似于 Popen3,但总是将标准错误捕获到与标准输出相同的文件对象中。 这些通常是使用 popen4() 创建的。
2.0 版中的新功能。
17.5.1. Popen3 和 Popen4 对象
- Popen3.poll()
- 如果子进程尚未完成,则返回
-1
,否则返回其状态代码(参见 wait())。
- Popen3.wait()
- 等待并返回子进程的状态码。 状态代码对进程的返回代码和有关它是使用
exit()
系统调用退出还是由于信号而死亡的信息进行编码。 os模块中定义了帮助解释状态码的函数; 有关W*()
函数系列的信息,请参阅 进程管理 部分。
还可以使用以下属性:
- Popen3.fromchild
- 提供子进程输出的文件对象。 对于 Popen4 实例,这将提供标准输出和标准错误流。
- Popen3.tochild
- 为子进程提供输入的文件对象。
- Popen3.childerr
- 如果构造函数的 capturestderr 为真,则提供子进程的错误输出的文件对象,否则为
None
。 对于 Popen4 实例,这将始终是None
。
- Popen3.pid
- 子进程的进程 ID。
17.5.2. 流量控制问题
任何时候处理任何形式的进程间通信时,都需要仔细考虑控制流。 对于此模块(或 os 模块等效项)提供的文件对象,情况仍然如此。
在父进程从子进程的标准输出中读取大量数据的子进程读取输出时,可能会发生死锁。 其他读取和写入组合也可能发生类似情况。 主要因素是一个进程以阻塞方式写入了超过 _PC_PIPE_BUF
个字节,而另一个进程正在以阻塞方式从第一个进程读取。
有几种方法可以处理这种情况。
在许多情况下,最简单的应用程序更改将是在父进程中遵循此模型:
import popen2
r, w, e = popen2.popen3('python slave.py')
e.readlines()
r.readlines()
r.close()
e.close()
w.close()
在孩子中使用这样的代码:
import os
import sys
# note that each of these print statements
# writes a single long string
print >>sys.stderr, 400 * 'this is a test\n'
os.close(sys.stderr.fileno())
print >>sys.stdout, 400 * 'this is another test\n'
特别要注意的是,写入所有数据后必须关闭 sys.stderr
,否则 readlines()
将不会返回。 另请注意,必须使用 os.close(),因为 sys.stderr.close()
不会关闭 stderr
(否则分配给 sys.stderr
将默默关闭它,所以不能打印更多的错误)。
需要支持更通用方法的应用程序应将管道上的 I/O 与其 select() 循环集成,或使用单独的线程读取由 popen*()
函数提供的每个文件或 Popen*
类被使用。