Intereting Posts
Встречайте проблемы при попытке разместить ASP.NET Web Api на Debian с помощью Mono и Nginx Графический интерфейс не входит в систему Есть ли способ перенаправить запросы от foo.com на виртуальную машину на этом сервере? Пользовательские настройки для monit? Должна ли она выполняться как root или ее собственный пользователь? Как сделать ssh в новую виртуальную машину? Как устранить старые приложения X, работающие неправильно на современных X-серверах? Почему я не могу переключить виртуальный терминал сейчас? объединение раздела жесткого диска Чтобы получить вывод столбцов в отдельный файл Vim: копирование из файла в буфер обмена Разница между 'cd' и 'cd ~' Печать значения столбца в следующей строке Crontab не дает результата Как открыть терминал при запуске скрипта оболочки? Наблюдение за записью жесткого диска в пространстве ядра (с драйверами / модулями)

Сравнить строку даты и времени

У меня есть файл, содержащий следующие данные (показаны только примеры данных. Файл будет содержать максимум 2001 строк)

0001:3002:2018/07/16:12.34.31:ERR 0002:3002:2018/07/16:12.34.44:ERR 0003:3002:2018/07/16:12.34.57:ERR 0004:3002:2018/07/16:12.35.10:ERR 0005:3002:2018/07/16:12.35.23:ERR 0006:3002:2018/07/16:12.35.36:ERR 0007:3002:2018/07/16:12.35.49:ERR 0008:3002:2018/07/16:12.36.02:ERR 0009:3002:2018/07/16:12.36.15:ERR 

Я передам дату, скажем, 2018/07/16: 12.36.15 скрипту bash. Я хочу прочитать каждую строку из этого файла и сравнить дату в строке с прошедшей датой и вернуть строку, дата которой больше, чем пройденная дата.

Что я уже сделал ?

 #!/bin/sh SEARCH_DATE=$1 errorCodeFilePath=/home/.errorfile.log lines=`cat $errorCodeFilePath` for line in $lines; do errorCodeDate=$(echo $line |grep -Eo '[[:digit:]]{4}/[[:digit:]]{2}/[[:digit:]]{2}:[[:digit:]]{2}.[[:digit:]]{2}.[[:digit:]]{2}'); if [ $errorCodeDate -ge $SEARCH_DATE ]; then echo $errorCodeDate fi done 

Вопросы

  1. Я не уверен, что сравнение дат будет работать? Я получаю сообщение об ошибке “ожидается целочисленное выражение”. Я буквально не знаю, как писать сценарии Bash, и это моя первая попытка.

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

Ваш скрипт читает весь файл в переменную и затем перебирает значение этой переменной. Это имеет три проблемы:

  1. В самом общем случае можно не знать размер входного файла, что означает, что при некоторых обстоятельствах переменная может стать очень большой.
  2. Цикл по значению переменной без кавычек будет зависеть от того, будет ли shell разбивать данные на пробелы (пробелы, табуляции и переводы строк). Если данные содержат какие-либо пробелы, кроме строк новой строки, цикл, вероятно, будет действовать неправильно.
  3. Оболочка будет выполнять подстановку имени файла над значениями переменной без кавычек перед циклом. Это означает, что если данные содержат шаблоны сгущения, такие как * или [...] , то они будут сопоставлены с существующими именами файлов.

В этом ответе используется тот факт, что используемые временные метки являются нормальными в том смысле, что более поздние метки времени сортируются после более ранних (по крайней мере, в локали POSIX).

 #!/bin/bash while IFS= read -r line; do timestamp=${line%:*} # Remove ":ERR" at the end timestamp=${timestamp#*:*:} # Remove numbers from start ("0001:3002:") if [[ "$timestamp" > "$1" ]]; then # According to the current locale, the timestamp in "$timestamp" # sorts after the timestamp in "$1". printf "Greater: %s\n" "$line" fi done  

Этот скрипт будет использовать метку времени того же формата, что и файл, в качестве единственного аргумента. Он перебирает содержимое файла и для каждой строки анализирует метку времени и сравнивает ее с меткой времени в командной строке. Сравнение выполняется с использованием оператора > в bash и будет истинным, если отметка времени в файле сортируется (лексикографически) после заданной отметки времени в текущей локали. Если сравнение верно, строка из файла печатается.

Две отдельные замены для анализа временной метки из строки путем удаления частей конца и начала строки можно заменить на

 timestamp=$( cut -d ':' -f 3,4 <<<"$line" ) 

но это будет работать медленнее, так как вызывает внешнюю утилиту.

Тестирование:

 $ bash script.sh '2018/07/16:12.36.00' Greater: 0008:3002:2018/07/16:12.36.02:ERR Greater: 0009:3002:2018/07/16:12.36.15:ERR 

Если вы хотите вывести только временную метку из файла, а не исходную строку, измените "$line" на "$timestamp" в команде printf .

В этом случае вы также можете ускорить процесс, выполнив цикл следующим образом:

 #!/bin/bash cut -d ':' -f 3,4 file | while IFS= read -r timestamp; do if [[ "$timestamp" > "$1" ]]; then # According to the current locale, the timestamp in "$timestamp" # sorts after the timestamp in "$1". printf "Greater: %s\n" "$timestamp" fi done 

Здесь мы используем cut чтобы получить 3-й и 4-й столбцы : -delimited из файла (временную метку), что означает, что нам не нужно выполнять parsing исходных строк.

Связанные с:

  • Понимание "IFS = read -r line"
  • Почему printf лучше, чем echo?
  • Когда необходимо двойное цитирование?

Ваша идея верна, но вы можете исправить некоторые вещи, чтобы скрипт работал так, как ожидалось.

  1. Во-первых, использование cat для файла, сохранение в переменной и циклическое повторение – в лучшем случае анти-паттерн. Подход сломал бы строки пробелом. Вместо этого используйте redirect файлов с циклом while.
  2. Всегда заключайте в кавычки переменные оболочки, чтобы сохранить содержимое переменной и предотвратить разделение слов, как упоминалось в предыдущем пункте.
  3. Вместо grep используйте встроенную поддержку регулярных выражений bash чтобы извлечь строку даты для преобразования EPOCH.
  4. По умолчанию bash не обеспечивает способ сравнения строк date , вам нужно преобразовать в эквивалентные значения EPOCH и сделать целочисленное сравнение

Так что все это вместе, без использования каких-либо сторонних инструментов и только с внутренними компонентами оболочки. Нужна команда date из утилит GNU для использования флага -d и может не работать в собственную date с машин * BSD.

 #!/usr/bin/env bash errorCodeFilePath="/home/.errorfile.log" re='[0-9]+/[0-9]+/[0-9]+:[0-9]+\.[0-9]+\.[0-9]+' convDateString() { day="${1##*:}" time="${1%%:*}" printf '%d' "$(date -d"$time ${day//./:}" +%s)" } while IFS= read -r line; do inputArg="$1" inputEPOCH="$(convDateString "${inputArg}")" if [[ $line =~ $re ]]; then lineEPOCH="$(convDateString "${BASH_REMATCH[*]}")" if [ "$lineEPOCH" -gt "$inputEPOCH" ]; then echo "${BASH_REMATCH[@]}" is greater fi fi done<"$errorCodeFilePath" 

Тестирование вашего файла на примере входного файла

 $ bash script.sh "2018/07/16:12.36.00" 2018/07/16:12.36.02 is greater 2018/07/16:12.36.15 is greater 

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

Попробуй это,

 #!/bin/sh SEARCH_DATE="$1" errorCodeFilePath=/home/nagios/temp/test1 lines=`cat $errorCodeFilePath` for line in $lines; do errorCodeDate=$(echo $line |grep -Eo '[[:digit:]]{4}/[[:digit:]]{2}/[[:digit:]]{2}:[[:digit:]]{2}.[[:digit:]]{2}.[[:digit:]]{2}'); if [ $(date -d "`echo $errorCodeDate| tr ':' ' '| tr '.' ':'`" +%s) -ge $(date -d "`echo $SEARCH_DATE| tr ':' ' '| tr '.' ':'`" +%s) ]; then echo $errorCodeDate fi done