将标准输出从 subprocess.Popen 保存到文件,并将更多内容写入文件

Posted

技术标签:

【中文标题】将标准输出从 subprocess.Popen 保存到文件,并将更多内容写入文件【英文标题】:Saving stdout from subprocess.Popen to file, plus writing more stuff to the file 【发布时间】:2011-03-12 13:46:55 【问题描述】:

我正在编写一个 python 脚本,它使用 subprocess.Popen 执行两个程序(来自已编译的 C 代码),每个程序都会产生标准输出。该脚本获取该输出并将其保存到文件中。因为输出有时大到足以压倒 subprocess.PIPE,导致脚本挂起,所以我将 stdout 直接发送到日志文件。我想让我的脚本在文件的开头和结尾以及两个 subprocess.Popen 调用之间写一些东西。但是,当我查看我的日志文件时,我从脚本写入日志文件的所有内容都放在文件的顶部,然后是所有可执行的标准输出。如何将添加的文本交错到文件中?

def run(cmd, logfile):
    p = subprocess.Popen(cmd, shell=True, universal_newlines=True, stdout=logfile)
    return p

def runTest(path, flags, name):
    log = open(name, "w")
    print >> log, "Calling executable A"
    a_ret = run(path + "executable_a_name" + flags, log)
    print >> log, "Calling executable B"
    b_ret = run(path + "executable_b_name" + flags, log)
    print >> log, "More stuff"
    log.close()

日志文件有: 调用可执行文件 A 调用可执行文件 B 更多东西 [...来自两个可执行文件的标准输出...]

例如,在调用 Popen 之后,有没有办法可以将 A 的标准输出刷新到日志中?还有一件可能相关的事情:可执行文件 A 开始然后在 B 上挂起,在 B 打印内容并完成之后,A 然后打印更多内容并完成。

我在 RHE Linux 上使用 Python 2.4。

【问题讨论】:

当我使用 stdout=subprocess.PIPE 并让外部循环将所有内容写入日志文件时,我能够将自己的文本与可执行文件的输出交错。当我不添加任何文本时,日志的内容按以下顺序排列:1)A 输出 2)B 输出 3)A 输出的其余部分。我可以在每个步骤之前或之后添加文本。现在我只能在日志的开头或结尾添加文本。在 Popen 挂起脚本之后添加一个 wait() ,因为 B 在 A 完成之前不会启动,它不会,因为 A 等待来自 B 的握手。是否可以用这种方法在日志中交错我自己的文本? 【参考方案1】:

据我了解,A 程序等待 B 执行其操作,而 A 仅在 B 退出后退出。

如果B 可以在不运行A 的情况下启动,那么您可以按相反的顺序启动进程:

from os.path import join as pjoin
from subprocess import Popen

def run_async(cmd, logfile):
    print >>log, "calling", cmd
    p = Popen(cmd, stdout=logfile)
    print >>log, "started", cmd
    return p

def runTest(path, flags, name):
    log = open(name, "w", 1)  # line-buffered
    print >>log, 'calling both processes'
    pb = run_async([pjoin(path, "executable_b_name")] + flags.split(), log)
    pa = run_async([pjoin(path, "executable_a_name")] + flags.split(), log)
    print >>log, 'started both processes'
    pb.wait()
    print >>log, 'process B ended'
    pa.wait()
    print >>log, 'process A ended'
    log.close()

注意:在主进程中调用log.flush() 对子进程中的文件缓冲区没有影响。

如果子进程对 stdout 使用块缓冲,那么您可以尝试使用 pexpect, pty, or stdbuf 强制它们更快地刷新(假设进程在交互运行时使用行缓冲,或者它们使用 C stdio 库进行 I/O) .

【讨论】:

【参考方案2】:

我说保持简单。伪代码基本逻辑:

write your start messages to logA
execute A with output to logA
write your in-between messages to logB
execute B with output to logB
write your final messages to logB
when A & B finish, write content of logB to the end of logA
delete logB

【讨论】:

感谢您的开箱即用建议,即为 A 和 B 使用两个单独的日志文件而不是单个日志文件。我得考虑一下。【参考方案3】:

您需要等到该过程完成后再继续。我还将代码转换为使用更简洁的上下文管理器。

def run(cmd, logfile):
    p = subprocess.Popen(cmd, shell=True, universal_newlines=True, stdout=logfile)
    p.wait()
    return p

def runTest(path, flags, name):
    with open(name, "w") as log:
        print >> log, "Calling executable A"
        a_ret = run(path + "executable_a_name" + flags, log)
        print >> log, "Calling executable B"
        b_ret = run(path + "executable_b_name" + flags, log)
        print >> log, "More stuff"

【讨论】:

上下文管理器是 python2.6 的一个特性,对于仍在运行 RHEL5 系统的任何人来说都是不可用的。在 RHEL6 出来之前,最好不要使用它们。 您可以在 Python 2.5 中使用上下文管理器,方法是在任何其他导入之前使用 from __future__ import with_statement 但 RHEL5 似乎卡在 Python 2.4 上。【参考方案4】:

您可以在每个 Popen 对象上调用 .wait() 以确保它已完成,然后调用 log.flush()。也许是这样的:

def run(cmd, logfile):
    p = subprocess.Popen(cmd, shell=True, universal_newlines=True, stdout=logfile)
    ret_code = p.wait()
    logfile.flush()
    return ret_code

如果您需要与外部函数中的 Popen 对象进行交互,您可以将 .wait() 调用移到那里。

【讨论】:

如果我在 run() 函数中放置了一个 wait(),那么可执行文件 B 在 A 完成之前不会开始运行,并且由于 A 在 B 完成之前不会完成,因此脚本会挂起。但是,我发现如果我有 runTest(),外部函数,运行 A 然后 B,然后等待 A 并刷新日志,我在 runTest 末尾打印的一行实际上显示在日志文件的末尾。在运行 B 之前,我仍然没有找到将文本打印到文件的方法。不知道有没有办法。 logfile.flush() 对子进程没有影响。 shell=True 不推荐

以上是关于将标准输出从 subprocess.Popen 保存到文件,并将更多内容写入文件的主要内容,如果未能解决你的问题,请参考以下文章

管道输出subprocess.Popen到文件

python中的subprocess.Popen()使用

带有标准输入的 subprocess.Popen.communicate() 的管道损坏

如何从 subprocess.Popen() 获取输出。 proc.stdout.readline() 块,没有数据打印出来

如何从 subprocess.Popen 使用 STDIN [重复]

Python如何从迭代中获取与subprocess.Popen一起运行的变量输入