您可以像往常一样使python子流程输出stdout和stderr,但也可以将输出捕获为字符串吗?

您可以像往常一样使python子流程输出stdout和stderr,但也可以将输出捕获为字符串吗?,python,subprocess,Python,Subprocess,可能重复: 在中,询问是否可能有一个python子进程输出到stdout,同时将输出保存在字符串中以供以后处理。这种情况下的解决方案是在每个输出行上循环并手动打印: output = [] p = subprocess.Popen(["the", "command"], stdout=subprocess.PIPE) for line in iter(p.stdout.readline, ''): print(line) output.append(line) 但是,此解决方

可能重复:

在中,询问是否可能有一个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.stdout
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]

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

i、 e


这个例子似乎适合我:

# -*- 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或类似的工具(如扭曲反应器)。

以可移植的方式打印到控制台并捕获子流程的字符串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\u call()
在中定义


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

我看不出这种技术(包装任何IO函数和流对象)如何不能通用化?您建议我如何让子进程实时打印到stdout和stderr,同时在最后以字符串形式获得输出,那么?用时间对线程排序是个坏主意。@Dani-这是两个截然不同的想法,时间一个不涉及线程…他们说你可以用wait()和poll()以及stdout(和stderr)的管道来实现死锁。顺序可能与原始顺序不完全相同,因为在调用
select
时,
stdout
stderr
可能都有数据,但这与我目前看到的解决方案非常接近。非常感谢。虽然不太可能,但你是对的,你的情况有可能发生。但我不相信有可能保证这一点。请注意,通常stdout启用了行缓冲,而stderr没有。它在Windows上不起作用。select()仅在Windows上接受套接字此答案有问题。如果在调用select()和调用poll()之间的流中有更多的数据变得可读,那么这些数据将永远不会被打印出来。这种方法在大多数情况下都有效,但我注意到,有时即使
p.poll()为None
,也不会将所有stdout/stderr内容都写入屏幕。我在proc.stdout/stderr.readlines()中为line添加了类似于
的内容:在中断前打印(line)
,它似乎可以工作。Poster希望在stdout/stderr中输入数据时处理数据,而不是在命令末尾。@ThomasVanderStichele:单击链接并查看
调用()
定义。它会尽快显示stdout/stderr。它不是
subprocess.call()
。这不起作用,至少不总是这样:
AttributeError:StringIO实例没有属性“fileno”
@KomodoDave:您确定使用了而不是
subprocess.call()
?我已将
call()
重命名为
teed\u call()
,以避免混淆(以防万一)。@KomodoDave你能删除你的误导性评论吗?代码确实有效。
from StringIO import StringIO

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