Я использую 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
. Я нашел / получил предложение о простом питонском способе, который отвечает моим потребностям.