在运行时获取进程的输出

10

我正在使用Python脚本通过subprocess.Popen运行进程,同时将输出存储在文本文件中并打印到控制台。 这是我的代码:

result = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)
for line in result.stdout.readlines(): #read and store result in log file
    openfile.write("%s\n" %line)
    print("%s" %line)

上述代码运行良好,但它的做法是首先完成整个过程并将输出存储在result变量中,然后for循环存储输出并打印输出。

但我希望能够在运行时得到输出(因为我的进程可能需要数小时才能完成,这几个小时内我无法得到任何输出)。

因此,是否有其他函数可以动态地给我输出(即当进程给出第一行时,应立即打印)。


1
我从未尝试过,但我认为你应该将自己的Python“file”对象发送到“stdout”(或“stdin”或“stderr”)参数。然后你必须轮询该文件。Subprocess是为了避免这种痛苦而发明的,但看起来你别无选择。祝你好运。 - Adrian Ratnapala
1
“它首先完成过程并将输出存储在结果中” - 这不是真的。 - Eric
1
“它首先完成进程并将输出存储在结果中” - 这不是真的。当我运行Popen命令时,它会一直运行,直到进程完成,然后才执行进一步的编码。如果有其他工作方式,我很想知道。 - Niyojan
@AdrianRatnapala 听起来太复杂了,而且“没有选择”的说法是错误的。 - glglgl
你可以考虑使用fcntl将result.stdout设置为非阻塞的,这样可以实时读取数据,如果还没有数据,就会引发IOError异常。 - EmilioPeJu
3个回答

9
这里的问题在于.readlines()会在返回结果之前获取所有输出,因为它构建了一个完整的列表。直接迭代即可:
for line in result.stdout:
    print(line)

1
请注意:在Python 2中,for line in result.stdout不会实时返回行,请改用for line in iter(result.stdout.readline, b'')。您的print line语句表明您希望它在Python 2上正常工作。在Python 3上它可以正常工作 - jfs
是的,它正在工作,我正在实时获取输出,谢谢。 - Niyojan

4

.readlines() 返回一个列表,其中包含打开过程中进程将返回的所有行,即在接收到子进程的所有输出之前,它不会返回任何内容。要实时逐行读取:

import sys
from subprocess import Popen, PIPE

proc = Popen(cmd, shell=True, bufsize=1, stdout=PIPE)
for line in proc.stdout:
    openfile.write(line)
    sys.stdout.buffer.write(line)
    sys.stdout.buffer.flush()
proc.stdout.close()
proc.wait()

注意:如果子进程在非交互模式下运行时使用块缓冲,则可能需要 pexpectpty 模块stdbufunbufferscript 命令
注意:在 Python 2 中,您可能还需要使用 iter() 来获取“实时”输出:
for line in iter(proc.stdout.readline, ""):
    openfile.write(line)
    print line,

1
你可以通过在管道上使用readline逐行迭代:
while True: 
    line = result.stdout.readline()
    print line.strip()
    if not line:
        break

这些行末尾包含一个换行符\n,我在打印时将其去掉。当进程终止时,readline返回一个空字符串,因此您知道何时停止。

1
对不起,但是你没有理解我的问题。你建议的我已经使用for循环完成了,但我想在进程仍在运行状态时逐行输出。 - Niyojan
1
你试过了吗?你正在使用readlines,它会返回所有行。这只有在终止后才可能发生。使用readline,你只能得到下一行。 - Thomas Fenzl
result.stdout 对我来说运行得非常好,但是你的 line.strip() 函数非常有用,因为在使用它之前我得到了太多的“b'\r\n'”,谢谢。 - Niyojan

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