Как наилучшим образом получить итоговый вывод прогресса процесса, содержащий возврат каретки - PullRequest
0 голосов
/ 26 января 2019

Я использую python для соединения нескольких инструментов обработки для задач НЛП вместе, но также собираю выходные данные каждого из них в случае сбоя и записываю его в журнал.

Некоторым инструментам требуется много часов, и их текущее состояние выводится в виде процента прогресса с возвратом каретки (\r). Они делают много шагов, поэтому они смешивают обычные сообщения и сообщения о прогрессе. Это приводит к иногда очень большим файлам журнала, которые трудно просмотреть с помощью less. Мой журнал будет выглядеть так (для быстрого прогресса):

[DEBUG  ] [FILE] [OUT] ^M4% done^M8% done^M12% done^M15% done^M19% done^M23% done^M27% done^M31% done^M35% done^M38% done^M42% done^M46% done^M50% done^M54% done^M58% done^M62% done^M65% done^M69% done^M73% done^M77% done^M81% done^M85% done^M88% done^M92% done^M96% done^M100% doneFinished

То, что я хочу, - это простой способ свернуть эти строки в python. (Я полагаю, что это также возможно сделать после завершения конвейера и заменить сообщения о ходе выполнения, например, на *. 1008 * ...)

Мой код для запуска и захвата вывода выглядит так:

import subprocess
from tempfile import NamedTemporaryFile

def run_command_of(command):
    try:
        out_file = NamedTemporaryFile(mode='w+b', delete=False, suffix='out')
        err_file = NamedTemporaryFile(mode='w+b', delete=False, suffix='err')
        debug('Redirecting command output to temp files ...', \
            'out =', out_file.name, ', err =', err_file.name)

        p = subprocess.Popen(command, shell=True, \
                             stdout=out_file, stderr=err_file)
        p.communicate()
        status = p.returncode

        def fr_gen(file):
            debug('Reading from %s ...' % file.name)
            file.seek(0)
            for line in file:
                # TODO: UnicodeDecodeError?
                # reload(sys)
                # sys.setdefaultencoding('utf-8')
                # unicode(line, 'utf-8')
                # no decoding ...
                yield line.decode('utf-8', errors='replace').rstrip()
            debug('Closing temp file %s' % file.name)
            file.close()
            os.unlink(file.name)
        return (fr_gen(out_file), fr_gen(err_file), status)
    except:
        from sys import exc_info
        error('Error while running command', command, exc_info()[0], exc_info()[1])
        return (None, None, 1)

def execute(command, check_retcode_null=False):
    debug('run command:', command)
    out, err, status = run_command_of(command)
    debug('-> exit status:', status)

    if out is not None:
        is_empty = True
        for line in out:
            is_empty = False
            debug('[FILE]', '[OUT]', line.encode('utf-8', errors='replace'))
        if is_empty:
            debug('execute: no output')
    else:
        debug('execute: no output?')
    if err is not None:
        is_empty = True
        for line in err:
            is_empty = False
            debug('[FILE]', '[ERR]', line.encode('utf-8', errors='replace'))
        if is_empty:
            debug('execute: no error-output')
    else:
        debug('execute: no error-output?')

    if check_retcode_null:
        return status == 0
    return True

Это некоторый старый код в Python 2 (забавное время со строками Unicode), который я хочу переписать в Python 3 и улучшить. ( Я также открыт для предложений о том, как обрабатывать вывод в режиме реального времени, а не когда все будет завершено. update : слишком широкий и не совсем часть моей проблемы )

Я могу придумать много подходов, но не знаю, есть ли готовая функция / библиотека / и т.д. но я не смог найти ни одного. (Мой гугл-фу нуждается в работе.) Единственное, что я нашел, были способы удаления CR / LF, но не часть строки, которая визуально заменяется. Так что я открыт для предложений и улучшений, прежде чем тратить свое время на повторную реализацию колеса. ; -)

Мой подход заключается в использовании регулярного выражения для поиска разделов в строке / строке между \r и их удаления. При желании я бы оставил одно процентное значение для действительно длинных процессов. Что-то вроде \r([^\r]*\r).


Примечание: Возможный дубликат: Как получить вывод самой последней команды терминала? Это может потребовать сценарий оболочки. Это может все еще использоваться, чтобы преобразовать мои старые файлы журнала с script2log. Я нашел / получил предложение о простом питонском способе, который отвечает моим потребностям.

1 Ответ

0 голосов
/ 26 января 2019

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

# my data
segments = ['abcdef', '567', '1234', 'xy', '\n']
s = '\r'.join(segments)

# really fast approach:
last = s.rstrip().split('\r')[-1]

# or: simulate the overwrites
parts = s.rstrip().split('\r')
last = parts[-1]
last_len = len(last)
for part in reversed(parts):
    if len(part) > last_len:
        last = last + part[last_len]
        last_len = len(last)

# result
print(last)

Благодаря комментариям к моему вопросу, я мог бы улучшить / усовершенствовать свои требования.В моем случае единственными управляющими символами являются возврат каретки (CR, \r), и довольно простое решение работает так, как предлагается tripleee .

Почему бы не просто последняя часть после \r?Вывод

echo -e "abcd\r12"

может привести к:

12cd

Вопросы под тегом подпроцесса (также предлагается в комментарии от tripleee ) должен помочь в выводе в реальном времени / чередовании, но находится за пределами моего текущего фокуса.Я должен буду проверить лучший подход.Я уже использовал stdbuf для переключения буферизации при необходимости.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...