【问题标题】:Saving stdout from subprocess.Popen to file, plus writing more stuff to the file将标准输出从 subprocess.Popen 保存到文件,并将更多内容写入文件
【发布时间】: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 的握手。是否可以用这种方法在日志中交错我自己的文本?

标签: python linux subprocess stdout python-2.4


【解决方案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 不推荐
    猜你喜欢
    • 1970-01-01
    • 2019-02-27
    • 2013-03-18
    • 1970-01-01
    • 2020-11-25
    • 2014-02-22
    • 1970-01-01
    • 1970-01-01
    • 2017-08-11
    相关资源
    最近更新 更多