Python:捕获subprocess.call的标准输出

8
我试图使用Python执行shell命令时完成两件事情:
  • 捕获stdout并在发生时打印
  • 在命令完成时捕获整个stdout并处理它
我查看了 subprocess.check_output,但它没有一个stdout参数可以让我实时打印输出。
所以,阅读了这个问题后,我意识到我可能需要尝试不同的方法。
from subprocess import Popen, PIPE

process = Popen(task_cmd, stdout = PIPE)
stdout, stderr = process.communicate()

print(stdout, stderr)

这种方法的问题在于,根据文档Popen.communicate()

读取stdout和stderr中的数据,直到达到文件结尾。 等待进程终止

我仍然无法将输出重定向到stdout和某种可以在完成命令时进行解析的缓冲区。

理想情况下,我希望有这样的东西:

# captures the process output and dumps it to stdout in realtime
stdout_capture = Something(prints_to_stdout = True)
process = Popen(task_cmd, stdout = stdout_capture)

# prints the entire output of the executed process
print(stdout_capture.complete_capture)

有没有推荐的方法来实现这个?

可能是Retrieving the output of subprocess.call()的重复问题。 - Random Davis
请查看https://dev59.com/gHRB5IYBdhLWcg3wc3A0。 - Kijewski
其他话题似乎只关注于简单地捕获输出;而我试图同时将输出转储到标准输出并完整捕获以便在完成后解析。我卡在了同时执行这两个操作上。 - doremi
2个回答

2
你在使用给予Popen stdout=PIPE 的方法上是正确的,但你不能使用 .communicate() ,因为它会在执行后返回值。相反地,我建议你从.stdout中读取。
获取生成的输出的唯一保证方式是逐个字符从管道中读取。以下是我的方法:
def passthrough_and_capture_output(args):
    import sys
    import subprocess

    process = subprocess.Popen(args, stdout=subprocess.PIPE, universal_newlines=True)
    # universal_newlines means that the output of the process will be interpreted as text
    capture = ""

    s = process.stdout.read(1)
    while len(s) > 0:
        sys.stdout.write(s)
        sys.stdout.flush()
        capture += s
        s = process.stdout.read(1)

    return capture

注意,逐个读取字符可能会产生很大的开销,因此如果您可以接受稍微滞后一点,我建议您将read(1)中的1替换为以批量输出的不同字符数。

最初的回答:

请注意,每次只读取一个字符可能会导致显着的开销,因此,如果您可以容忍略有滞后,建议您将read(1)中的1替换为以批量输出的不同字符数。


-2
from subprocess import check_output, CalledProcessError

def shell_command(args):
    try:
        res = check_output(args).decode()
    except CalledProcessError as e:
        res = e.output.decode()
    for r in ['\r', '\n\n']:
        res = res.replace(r, '')
    return res.strip()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接