Вопрос или проблема
Просмотр журналов в моей текущей компании оставляет желать лучшего. Я хочу добавить новую строку в журналы, которые я обрезал на определенный период времени. Я хочу новую строку после каждой даты-времени.
Формат даты-времени выглядит так.
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 для подобных задач, так как они предоставляют удобную обработку текстов, и их легко внедрить в автоматизированные процессы обработки логов.
Таким образом, интеграция одного из этих методов в вашу повседневную практику работы с логами может значительно удешевить и ускорить выполнение задач, связанных с обработкой большого объема текстового контента.