33

可能重复:
包装子进程的 stdout/stderr

这个问题中,hanan-n询问是否有可能有一个 python 子进程输出到 stdout,同时还将输出保存在字符串中以供以后处理。这种情况下的解决方案是遍历每个输出行并手动打印它们:

output = []
p = subprocess.Popen(["the", "command"], stdout=subprocess.PIPE)
for line in iter(p.stdout.readline, ''):
    print(line)
    output.append(line)

但是,此解决方案并不能推广到您希望对 stdout 和 stderr 都执行此操作的情况,同时满足以下条件:

  • stdout/stderr 的输出应该分别到父进程的 stdout/stderr
  • 输出应尽可能实时完成(但我只需要访问最后的字符串)
  • 不应更改 stdout 和 stderr 行之间的顺序(我不太确定如果子进程以不同的时间间隔刷新其 stdout 和 stderr 缓存,这将如何工作;让我们现在假设我们将所有内容都放在包含完整的漂亮块中线?)

我查看了子流程文档,但找不到任何可以实现这一目标的东西。我能找到的最接近的方法是添加stderr=subprocess.stdout和使用与上面相同的解决方案,但是我们失去了常规输出和错误之间的区别。有任何想法吗?我猜这个解决方案——如果有的话——将涉及对p.stdoutand进行异步读取p.stderr

这是我想做的一个例子:

p = subprocess.Popen(["the", "command"])
p.wait()  # while p runs, the command's stdout and stderr should behave as usual
p_stdout = p.stdout.read()  # unfortunately, this will return '' unless you use subprocess.PIPE
p_stderr = p.stderr.read()  # ditto
[do something with p_stdout and p_stderr]
4

3 回答 3

37

这个例子似乎对我有用:

# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4

import subprocess
import sys
import select

p = subprocess.Popen(["find", "/proc"],
    stdout=subprocess.PIPE, stderr=subprocess.PIPE)

stdout = []
stderr = []

while True:
    reads = [p.stdout.fileno(), p.stderr.fileno()]
    ret = select.select(reads, [], [])

    for fd in ret[0]:
        if fd == p.stdout.fileno():
            read = p.stdout.readline()
            sys.stdout.write('stdout: ' + read)
            stdout.append(read)
        if fd == p.stderr.fileno():
            read = p.stderr.readline()
            sys.stderr.write('stderr: ' + read)
            stderr.append(read)

    if p.poll() != None:
        break

print 'program ended'

print 'stdout:', "".join(stdout)
print 'stderr:', "".join(stderr)

一般来说,任何你想同时处理多个文件描述符并且你不知道哪一个有东西供你阅读的情况,你应该使用 select 或类似的东西(比如 Twisted reactor)。

于 2012-09-04T22:33:20.620 回答
11

以可移植的方式打印到控制台并在子进程的字符串 stdout/stderr 中捕获:

from StringIO import StringIO

fout, ferr = StringIO(), StringIO()
exitcode = teed_call(["the", "command"], stdout=fout, stderr=ferr)
stdout = fout.getvalue()
stderr = ferr.getvalue()

teed_call()Python子进程中定义的地方是获取孩子的输出到文件和终端?

您可以使用任何类似文件的对象(.write()方法)。

于 2012-09-05T18:25:10.807 回答
2

如上所述创建两个阅读器,stdout一对一用于stderr并在新线程中启动每个阅读器。这将以与进程输出的顺序大致相同的顺序附加到列表中。如果需要,请维护两个单独的列表。

IE,

p = subprocess.Popen(["the", "command"])
t1 = thread.start_new_thread(func,stdout)  # create a function with the readers
t2 = thread.start_new_thread(func,stderr)
p.wait() 
# your logic here
于 2012-09-04T20:27:54.117 回答