Скрипт SGE: печатать в файл во время выполнения (а не только в конце)? - PullRequest
5 голосов
/ 26 марта 2012

У меня есть сценарий SGE для выполнения некоторого кода Python, переданного в очередь с использованием qsub. В скрипте Python у меня есть несколько операторов печати (информирует меня о ходе работы программы). Когда я запускаю скрипт python из командной строки, операторы печати отправляются на стандартный вывод. Для сценария sge я использую опцию -o, чтобы перенаправить вывод в файл. Тем не менее, кажется, что скрипт будет отправлять их в файл только после завершения работы скрипта python. Это раздражает, потому что (а) я больше не вижу обновления программы в режиме реального времени и (б) если моя работа не завершается правильно (например, если моя работа удаляется из очереди), ни одно из обновлений не печатается. Как я могу убедиться, что скрипт записывает в файл каждый раз, когда я хочу что-то напечатать, а не объединять все вместе в конце?

Ответы [ 7 ]

7 голосов
/ 26 марта 2012

Я думаю, что вы столкнулись с проблемой с буферизованным выводом. Python использует библиотеку для обработки своего вывода, и библиотека знает, что более эффективно писать блок в то время, когда он не разговаривает с tty.

Есть несколько способов обойти это. Вы можете запустить python с опцией "-u" (см. Man-страницу python для подробной информации), например, с такой строкой, как эта, в первой строке вашего скрипта:

#! /usr/bin/python -u

но это не работает, если вы используете трюк "/ usr / bin / env", потому что вы не знаете, где установлен python.

Другой способ - открыть stdout примерно так:

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) 

Обратите внимание на параметр bufsize os.fdopen, установленный в 0, чтобы заставить его быть небуферизованным. Вы можете сделать что-то подобное с sys.stderr.

5 голосов
/ 18 декабря 2015

Как уже упоминалось, из соображений производительности не всегда пишется стандартный вывод, если он не подключен к tty.

Если у вас есть конкретная точка, в которую вы хотите записать стандартный вывод, вы можете принудительно сделать это, используя

import sys
sys.stdout.flush()

в этот момент.

3 голосов
/ 11 марта 2013

Это SGE, буферизирующий вывод вашего процесса, это происходит, будь то процесс Python или любой другой.

Как правило, вы можете уменьшить или отключить буферизацию в SGE, изменив ее и перекомпилировав.Но это не очень хорошая вещь, все эти данные будут медленно записываться на диск, влияя на вашу общую производительность.

3 голосов
/ 29 октября 2012

Я только что столкнулся с подобной проблемой с SGE, и не предлагал метод для "снятия буфера" файла IO, который, казалось, работал для меня.Мне пришлось ждать до конца выполнения программы, чтобы увидеть какие-либо выходные данные.

Обходной путь, который я нашел, заключался в том, чтобы обернуть sys.stdout в пользовательский объект, который повторно реализует метод write.Вместо фактической записи в стандартный вывод этот новый метод вместо этого открывает файл, куда перенаправляется ввод-вывод, добавляет необходимые данные и затем закрывает файл.Это немного уродливо, но я нашел, что это решило проблему, так как фактическое открытие / закрытие файла заставляет 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()
1 голос
/ 24 декабря 2015

Почему бы не печатать в файл вместо стандартного вывода?

outFileID = open('output.log','w')
print(outFileID,'INFO: still working!')
print(outFileID,'WARNING: blah blah!')

и использование

tail -f output.log
0 голосов
/ 28 июля 2017

Я столкнулся с этой же проблемой сегодня и решил ее, просто записав на диск вместо печати:

with open('log-file.txt','w') as out:
  out.write(status_report)
0 голосов
/ 20 июля 2015

Это работает для меня:

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.

...