将标准输出从 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.communicate() 的管道损坏
如何从 subprocess.Popen() 获取输出。 proc.stdout.readline() 块,没有数据打印出来