Как добавить пустую строку после строк, начинающихся с метки времени

Вопрос или проблема

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

Формат даты-времени выглядит так.

2023-10-03 15:34:37

Я знаю, что tr может это сделать, но я не уверен, как. Я выберу лучший ответ того, кто сможет сказать мне, как записать это в новый файл. Из-за производительности я не предпочитаю использовать vi, я использую less как средство просмотра журналов.

В vim вы могли бы сделать:

:%s/\v^\d{4}(-\d{2}){2} \d{2}(:\d{2}){2}.*/&\r

Обратите внимание на \v, чтобы строки не нужно было экранировать перед каждым {}().

Да, \r в замене добавляет новую строку (разделяет строку на две части, как говорится в :help s/\r), а не возврат каретки, как можно было бы ожидать.

В стандартном vi (\d, \v, \r, norm — это все расширения vim), эквивалент выглядел бы так:

:%s/^[[:digit:]]\{4\}\(-[[:digit:]]\{2\}\)\{2\} [[:digit:]]\{2\}\(:[[:digit:]]\{2\}\)\{2\}.*/&^M/

Где ^M вводится с помощью нажатия Ctrl+V, а затем Enter (и опять-таки вставляет LF, также известный как новая строка, также известный как ^J, а не CR, также известный как возврат каретки, также известный как ^M).

Но реализация vi на CentOS является vim (и была таковой, по крайней мере, начиная с CentOS 4), так что вы можете полагаться на расширения vim в vi там.

В любом случае tr — это инструмент для транслитерации, замены символов другими символами или удаления или сжатия их, его нельзя использовать здесь.

sed, awk или perl могут быть использованы (насколько я понимаю, \d как оператор регулярных выражений появился в perl):

perl -pi -e '$_ .= "\n" if /^\d{4}(-\d{2}){2} \d{2}(:\d{2}){2}/' your-file

Чтобы отредактировать файл на месте. Или:

perl -p -e '$_ .= "\n" if /^\d{4}(-\d{2}){2} \d{2}(:\d{2}){2}/' < your-file > new-file

Чтобы получить результат в новый файл.

Или здесь, если модуль Regexp::Common::time доступен:

perl -MRegexp::Common=time -p -e '
  $_ .= "\n" if m{^$RE{time}{strftime}{-pat=>"%Y-%m-%d %H:%M:%S"}}
  ' < your-file > new-file

Который обеспечит более строгое соответствие (например, не будет соответствовать 2023-13-00 25:61:62). Также имеется $RE{time}{iso}, который будет соответствовать этим датам, но также множеству других вариантов дат ISO8601, включая некоторые без компонента даты или времени, поэтому его нельзя использовать здесь.

Вы можете посмотреть фактическое регулярное выражение с помощью:

$ perl -MRegexp::Common=time -le 'print $RE{time}{strftime}{-pat=>"%Y-%m-%d %H:%M:%S"}'
(?:(?:\d{4})-(?:(?:(?=[01])(?:0[1-9]|1[012])))-(?:(?:(?=[0123])(?:0[1-9]|[12]\d|3[01]))) (?:(?:(?=[012])(?:[01]\d|2[0123]))):(?:(?:[0-5]\d)):(?:(?:(?=[0-6])(?:[0-5]\d|6[01]))))

Эта команда:

Нажмите : и введите
g/\v\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}/normal o

Объяснение:

  • g: сокращение от global, выполняет команду на всех строках, подходящих под регулярное выражение
  • \v: делает регулярное выражение ‘очень волшебным’ (это фактическое название). Это означает, что вам не нужно экранировать специальные символы
  • /^\d\{4\}-\d\{2\}-\d\{2\} \d\{2\}:\d\{2\}:\d\{2\}/: это регулярное выражение. Посадите его в такой инструмент, как https://regexr.com/ или https://regex101.com/, они очень хорошо его объясняют
  • normal o: нормальная команда выполняет аргументы в нормальном режиме. В этом случае она нажимает o, что открывает новую строку


Прочтите объяснения в официальной документации:
:help global, :help \v, :help normal

Используя Raku (ранее известен как Perl_6)

~$ echo '2023-10-03 15:34:37' | raku -ne '.subst(/ \s /, "T").DateTime.say'
2023-10-03T15:34:37Z

#get Type with `WHAT`:

~$ echo '2023-10-03 15:34:37' | raku -ne '.subst(/ \s /, "T").DateTime.WHAT.say'
(DateTime)

Из вышеупомянутого можно видеть, что Raku распознает формат DateTime ISO 8601 (даже без добавления информации о временной зоне). Поэтому, если вы преобразуете входной текст (замените одиночный пробел на T), и добавите Z для временной зоны (просто для надежности — используйте свой локальный ЧЗ), формат ISO 8601 DateTime будет узнан. Это работает:

Пример входных данных (1):

~$ echo '2023-10-03 15:34:37StuffIWantToMoveDown\nNextLine\nNextNextLine' > test.log
~$ cat test.log
2023-10-03 15:34:37StuffIWantToMoveDown
NextLine
NextNextLine
2023-10-03dateplusjunk

Код и пример вывода (1):

~$ raku -pe 's/^ (\d**4 \- \d**2 \- \d**2) ( \s )  (\d**2 \: \d**2 \: \d**2) /{DateTime("$0"~"T"~$2~"Z") ~ "\n\n"}/;' test.log
2023-10-03T15:34:37Z

StuffIWantToMoveDown
NextLine
NextNextLine
2023-10-03dateplusjunk

ИЛИ:

Код и пример вывода (2):

~$ raku -ne 'put S/^ (\d**4 \- \d**2 \- \d**2) ( \s )  (\d**2 \: \d**2 \: \d**2) /{DateTime("$0"~"T"~$2~"Z") ~ "\n\n"}/;' test.log
2023-10-03T15:34:37Z

StuffIWantToMoveDown
NextLine
NextNextLine
2023-10-03dateplusjunk

Но… я думаю, вам предпочтительнее было бы обрамить с обеих сторон стартовой строки DateTime, чтобы сделать ее более удобной для чтения, например, так (дублируйте ввод, чтобы он выглядел как запись):

Пример входных данных (финальный):

~$ echo '2023-10-03 15:34:37StuffIWantToMoveDown\nNextLine\nNextNextLine\n2023-10-03dateplusjunk' >> test.log

Код и пример вывода (финальный):

~$ raku -pe 's/^ (\d**4 \- \d**2 \- \d**2) ( \s )  (\d**2 \: \d**2 \: \d**2) /{"\n" ~ DateTime("$0"~"T"~$2~"Z") ~ "\n"}/;' test.log

2023-10-03T15:34:37Z
StuffIWantToMoveDown
NextLine
NextNextLine
2023-10-03dateplusjunk

2023-10-03T15:34:37Z
StuffIWantToMoveDown
NextLine
NextNextLine
2023-10-03dateplusjunk

https://www.iso.org/iso-8601-date-and-time-format.html
https://docs.raku.org/type/DateTime
https://raku.org

.

Ответ или решение

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

Теоретическая часть

В большинстве случаев такие задачи решаются с применением регулярных выражений. Регулярные выражения являются мощным инструментом для работы с текстом: они позволяют находить и модифицировать текстовые шаблоны. В данном случае, вам нужно найти строки, начинающиеся с временной метки в формате YYYY-MM-DD HH:MM:SS, и добавить после них пустую строку.

Примеры

Vim и vi: Если ваш редактор текста поддерживает расширения vim, можно использовать следующую команду для добавления пустой линии:

:%s/\v^\d{4}(-\d{2}){2} \d{2}(:\d{2}){2}.*/&\r

В стандартном vi, где нет расширений vim, будет необходимо использовать:

:%s/^[[:digit:]]\{4\}\(-[[:digit:]]\{2\}\)\{2\} [[:digit:]]\{2\}\(:[[:digit:]]\{2\}\)\{2\}.*/&^M/

Здесь ^M вводится комбинацией клавиш Ctrl+V затем Enter.

Однако, как вы упомянули, vi вам не подходит из-за соображений производительности. Рассмотрим другие инструменты.

Perl: Perl отлично подходит для обработки текстов, и следующий скрипт поможет добавить пустую строку:

perl -p -e '$_ .= "\n" if /^\d{4}(-\d{2}){2} \d{2}(:\d{2}){2}/' < ваш-файл > новый-файл

Этот скрипт читает файл и создает новый файл с добавленными пустыми строками.

Sed или Awk: Эти утилиты также могут быть использованы, но требуют чуть более сложного написания. Например, с помощью sed:

sed -e '/^[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\} [0-9]\{2\}:[0-9]\{2\}:[0-9]\{2\}/a\' -e '\\n' ваш-файл > новый-файл

Применение

Применение этих команд в вашем рабочем процессе может значительно улучшить читаемость логов. После того как вы выбрали наиболее подходящий инструмент для обработки ваших логов, интегрируйте его в скрипт автоматизации. Это позволит вам регулярно и быстро обрабатывать файлы лога, не прибегая к ручным редактированиям, что экономит время и снижает вероятность ошибок.

Дополнительно, если вы используете less для чтения логов, то может оказаться полезным дополнить скрипт соответствующей командой вывода в less после преобработки файла, например:

perl -p -e '...' < ваш-файл | less

Заключение

Каждый из описанных методов имеет свои достоинства и недостатки в зависимости от ваших конкретных нужд и доступных ресурсов. Специалисты по IT должны учитывать множество факторов, начиная от объёма обрабатываемых данных и заканчивая доступными инструментами и средой выполнения, прежде чем выбирать наиболее подходящий метод. Рекомендуется использовать Perl или sed для подобных задач, так как они предоставляют удобную обработку текстов, и их легко внедрить в автоматизированные процессы обработки логов.

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

Оцените материал
Добавить комментарий

Капча загружается...