我有一个 SGE 脚本来执行一些 python 代码,使用 qsub 提交到队列。在 python 脚本中,我有一些打印语句(向我更新程序的进度)。当我从命令行运行 python 脚本时,打印语句被发送到标准输出。对于 sge 脚本,我使用 -o 选项将输出重定向到文件。但是,似乎脚本只会在 python 脚本完成运行后将这些发送到文件中。这很烦人,因为 (a) 我无法再看到程序的实时更新,并且 (b) 如果我的作业没有正确终止(例如,如果我的作业被踢出队列),则不会打印任何更新。我如何确保每次我想打印某些东西时脚本都在写入文件,而不是在最后将它们混为一谈?
7 回答
我认为您遇到了缓冲输出的问题。Python 使用一个库来处理它的输出,并且该库知道在它不与 tty 对话时编写一个块更有效。
有几种方法可以解决这个问题。例如,您可以使用“-u”选项运行 python(有关详细信息,请参见 python 手册页),例如,将类似这样的内容作为脚本的第一行:
#! /usr/bin/python -u
但如果您使用“/usr/bin/env”技巧,这将不起作用,因为您不知道 python 的安装位置。
另一种方法是重新打开标准输出,如下所示:
import sys
import os
# reopen stdout file descriptor with write mode
# and 0 as the buffer size (unbuffered)
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
请注意将 os.fdopen 的 bufsize 参数设置为 0 以强制其无缓冲。你可以用 sys.stderr 做类似的事情。
正如其他人所提到的,在未连接到 tty 时并不总是写入标准输出是出于性能原因。
如果您有一个特定的点想要写入标准输出,您可以通过使用强制执行该点
import sys
sys.stdout.flush()
在那时候。
我刚刚遇到了与 SGE 类似的问题,并且没有建议的方法来“取消缓冲”文件 IO 似乎对我有用。我不得不等到程序执行结束才能看到任何输出。
我发现的解决方法是将 sys.stdout 包装到重新实现“写入”方法的自定义对象中。这个新方法不是实际写入标准输出,而是打开重定向 IO 的文件,附加所需的数据,然后关闭文件。这有点难看,但我发现它解决了问题,因为文件的实际打开/关闭强制 IO 是交互式的。
这是一个最小的例子:
import os, sys, time
class RedirIOStream:
def __init__(self, stream, REDIRPATH):
self.stream = stream
self.path = REDIRPATH
def write(self, data):
# instead of actually writing, just append to file directly!
myfile = open( self.path, 'a' )
myfile.write(data)
myfile.close()
def __getattr__(self, attr):
return getattr(self.stream, attr)
if not sys.stdout.isatty():
# Detect redirected stdout and std error file locations!
# Warning: this will only work on LINUX machines
STDOUTPATH = os.readlink('/proc/%d/fd/1' % os.getpid())
STDERRPATH = os.readlink('/proc/%d/fd/2' % os.getpid())
sys.stdout=RedirIOStream(sys.stdout, STDOUTPATH)
sys.stderr=RedirIOStream(sys.stderr, STDERRPATH)
# Simple program to print msg every 3 seconds
def main():
tstart = time.time()
for x in xrange( 10 ):
time.sleep( 3 )
MSG = ' %d/%d after %.0f sec' % (x, args.nMsg, time.time()-tstart )
print MSG
if __name__ == '__main__':
main()
这是 SGE 缓冲进程的输出,无论它是 python 进程还是其他任何进程都会发生。
通常,您可以通过更改和重新编译来减少或禁用 SGE 中的缓冲。但这不是一件好事,所有数据都会慢慢写入磁盘,影响您的整体性能。
为什么不打印到文件而不是标准输出?
outFileID = open('output.log','w')
print(outFileID,'INFO: still working!')
print(outFileID,'WARNING: blah blah!')
并使用
tail -f output.log
这对我有用:
class ForceIOStream:
def __init__(self, stream):
self.stream = stream
def write(self, data):
self.stream.write(data)
self.stream.flush()
if not self.stream.isatty():
os.fsync(self.stream.fileno())
def __getattr__(self, attr):
return getattr(self.stream, attr)
sys.stdout = ForceIOStream(sys.stdout)
sys.stderr = ForceIOStream(sys.stderr)
这个问题与 NFS 在关闭文件或调用 fsync 之前不会将数据同步回主服务器有关。
我今天遇到了同样的问题,并通过写入磁盘而不是打印来解决它:
with open('log-file.txt','w') as out:
out.write(status_report)