Сравните строку DateTime

Чтобы понять ответ на этот вопрос, вам нужно иметь некоторое представление о том, как обрабатывается ввод с клавиатуры. Я отсылаю вас к Как работают ввод и вывод текста с клавиатуры? для справки. В этом ответе я собираюсь объяснить соответствующую часть по-другому, но я буду исходить из общего понимания, данного в моем предыдущем ответе.

Мой ответ здесь касается типичных unix-систем; не-unix-системы могут вести себя по-другому. Я сделаю некоторые упрощения здесь и там; дополнительные сложности не имеют отношения к ответу на этот вопрос. (Этот ответ и так достаточно сложен).

Большинство коммуникаций и хранения данных, включая коммуникацию между терминальным приложением и терминалом (аппаратным или программным), принимают форму потока байтов. байт - это единица информации, которая может принимать 256 различных значений; она может быть разделена на 8 битов. Байты представляются числом от 0 до 255.

Чтобы передать информацию, стороны должны договориться о способе кодирования этой информации в виде байтов. Существует множество способов кодирования потоков символов в виде потоков байтов, но все они так или иначе основаны на ASCII. ASCII определяет соответствие между 7-битовыми значениями от 0 до 127 и набором из 128 символов; при этом в каждом байте остается неиспользуемый бит. 128 символов делятся на две категории:

  • 95 печатаемых символов: буквы (A-Z, строчные и прописные), цифры (0-9), пробел, некоторые знаки препинания;
  • 33 управляющих символа.

Управляющие символы кодируют приказы и вспомогательную информацию, отправляемую на терминал или с терминала, например, "переместите курсор на следующую строку", "позвоните в звонок", "подождите меня", "до свидания" и т.д. В исторических терминалах появилась клавиша "Control" (или сокращенно "Ctrl"), которая позволяла пользователям вводить управляющие символы. Чтобы упростить электронику терминала, нажатие Ctrl вместе с клавишей выполняло простую битовую маску в значении байта, обычно передаваемом символом. Например, A посылает символ A, представленный байтом 65 (1000001 в двоичном формате); Ctrl+A посылает символ, представленный значением байта 1 (0000001 в двоичном формате), который известен как символ "control-A", часто записываемый ^A.

Большинство управляющих символов соответствует заглавной букве с битовой схемой 10xxxxx, причем бит 6 установлен в 0, а не в 1. Это составляет 26 символов. Еще 6 управляющих символов соответствуют знакам препинания, которые также имеют битовую схему вида 10xxxxx: это @[\]^_ (см. Таблица печатных символов ASCII). В дополнение к диапазону 0-31, символ 127 также является управляющим символом; он известен как "control-?". (? - 0111111; control-? - 1111111).

С годами значениям байтов, не относящихся к стандарту ASCII, присваивались различные значения. Мир сходится к Unicode как набору всех символов, которые могут понадобиться кому-либо. Мир Unix (а также Интернет) в основном стандартизировал UTF-8 как способ кодирования символов в виде последовательностей байтов. UTF-8 поддерживает совместимость с ASCII, присваивая любому байту в диапазоне 0-127 тот же символ, что и ASCII, и используя последовательности от 2 до 4 байт в диапазоне 128-255 для представления миллиона или около того других символов. В мире unix используются некоторые другие кодировки символов; большинство из них основаны на ASCII и имеют различные значения для байтов больше 128.

Теперь я могу ответить на один из ваших последующих вопросов:

как набрать (1-байтовые) символы в диапазоне ASCII от \0200 до \0377

Это зависит от того, какую кодировку вы используете. Если вы используете UTF-8, наиболее распространенную, то отправка этих отдельных байтов невозможна, поскольку они используются только как часть последовательностей из 2-4 байтов, которые представляют один символ.

Что касается

списка A: все возможные значения директивы escape

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

В описании команды bind объясняется, как указывать значения байтов так, чтобы Screen понимал их. Там, где в документации написано "символ", вместо этого читайте "байт".

Прежде чем мы перейдем к списку B, нам нужно понять, что такое кейкорды. Клавишный аккорд - это нажатие клавиши вместе с модификаторами, такими как Ctrl, Shift и т.д. Ранее мы видели, что вся информация, передаваемая терминалом, кодируется в виде потока байтов. Для простоты все печатаемые символы кодируются стандартным образом - как один байт в диапазоне 32-126, если это символы ASCII, и как байт в диапазоне 128-255 для других символов. Таким образом, остаются только управляющие символы для кодирования функциональных клавиш и символов с модификаторами, отличными от Shift. Но управляющих символов всего 33!

Несколько функциональных клавиш передают управляющий символ. Например, клавиша Tab посылает ^I (значение байта 9), потому что байт 9 - это управляющий символ TAB, который дает команду принтеру перейти к следующей колонке табуляции. Клавиша Return посылает ^M (значение байта 13), потому что байт 13 - управляющий символ CR, который дает команду принтеру переместить головку в начало строки. По аналогичным причинам Escape посылает ^[, а BackSpace посылает либо ^H, либо ^? из-за некоторых исторических разночтений, которые я не буду здесь обсуждать.

Большинство функциональных клавиш и клавиатур посылают escape sequence: последовательность байтов, начинающуюся с байта 27, escape character (ESC), определенного ASCII, который имеет вид ^[ (control-[). Разные терминалы посылают разные управляющие последовательности. Существуют стандарты, но они не определяют кодировки для всех ключевых слов, это далеко не так, и в некоторой степени существуют конкурирующие стандарты.

Теперь мы готовы понять

список B: все возможные значения параметра в выражении bindkey -k

В документации Screen объясняется, что эти коды - termcap имена возможностей клавиатуры. Termcap - это библиотека программирования, которую приложения могут использовать, чтобы абстрагироваться от различий между терминалами. (Сейчас она в основном вытеснена Terminfo.) База данных Termcap содержит информацию о терминале, такую как количество строк и столбцов (во времена появления Termcap терминалы были аппаратными устройствами, к которым концепция изменения размера была неприменима), последовательности байт (часто начинающиеся с ESC), которые приложение может использовать для выполнения таких операций, как перемещение курсора или очистка экрана, и последовательности байт, посылаемые различными клавишами. Символьные имена, присвоенные Termcap функциональным клавишам, - это то, что вы можете использовать после bindkey -k.

В руководстве по Termcap перечислены все записи в этой базе данных. Все записи имеют двухсимвольные имена; в руководстве FreeBSD для каждой записи также дано несколько более выразительное имя. Записи, в которых FreeBSD перечисляет key_SOMETHING в первых колонках, описывают функциональные клавиши; , который нужен для bindkey -k - это имя во второй колонке, например, kl для Left, k1 для F1, F1 для F11 и т.д.

Вы заметите, что в этой базе данных не хватает многих кейкордов. Если в этой базе данных нет записи для кейкорда, то нет и имени, которое можно использовать для клавиши с помощью bindkey -k. Обратите внимание, что набор поддерживаемых ключей варьируется от варианта unix к варианту unix.

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

Вы можете узнать, какую управляющую последовательность посылает ключевой аккорд, нажав Ctrl+V, затем ключевой аккорд. В терминале в режиме по умолчанию, а также в командной строке всех распространенных оболочек, Ctrl+V означает "интерпретировать следующий байт буквально". Если за ним следует управляющая последовательность, это приводит к тому, что байт ESC будет вставлен буквально, а не инициирует разбор управляющей последовательности. Поскольку управляющие последовательности почти всегда состоят из печатаемых символов после ESC, это эффективно вставляет управляющую последовательность буквально. Например, нажмите Ctrl+V, затем Ctrl+Left, чтобы увидеть, какую управляющую последовательность посылает Ctrl+Left: вы увидите что-то вроде ^[O5D, где ^[ - это визуальное представление управляющего символа ESC. (Опять же, ваш терминал может посылать другую управляющую последовательность).

Что касается настройки нуля, когда Screen считывает байт ESC, он переходит в режим разбора управляющей последовательности. Каждый новый байт добавляется к накопленной управляющей последовательности. Если накопленная последовательность имеет связанную привязку, Screen выходит из режима разбора управляющей последовательности и отменяет привязку. Если накопленная последовательность не является префиксом какой-либо последовательности с привязкой, Screen выходит из режима разбора управляющей последовательности, а накопленная последовательность отбрасывается. Таким образом, установка null здесь является сложной формой "ничего не происходит".

После всей этой работы давайте обратимся к

списку C: отображение любой пары (a, b) (где aA, а bB) в явное описание того, как можно набрать соответствующий символ команды GNU-screen на стандартной американской клавиатуре, и предполагая конкретный эмулятор терминала

Как я намекнул выше, конкретный эмулятор терминала здесь важен: различные терминалы кодируют ключевые слова по-разному, и некоторые терминалы могут быть настроены по-разному. Сопоставление не соответствует паре (a, b): A × B не является интересным множеством. Большинство ключевых аккордов отображаются на либо печатаемый символ (который, как мы видели выше, расширяет A) либо управляющую последовательность (которая, как мы видели выше, расширяет B). Другими словами, отображение происходит на супермножество AB.

К сожалению для вас, многие терминалы не полностью документируют, как посылать управляющие последовательности. К счастью для вас, это редко требуется. Вместо того чтобы работать от управляющей последовательности к ключевому аккорду, работайте от ключевого аккорда к управляющей последовательности. Это можно определить для каждого терминала с помощью Ctrl+V, как описано выше.

Некоторые терминалы, в частности xterm, могут быть настроены на систематическое кодирование ключевых слов. См. Проблемы с привязками клавиш при использовании терминала для обсуждения, ориентированного на Emacs. К сожалению, этот не включает библиотеку vte, которую используют многие эмуляторы терминала, особенно в мире GNOME.

1
08.11.2019, 12:22
5 ответов

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

  1. В самом общем случае размер входного файла может быть неизвестен, а это означает, что при некоторых обстоятельствах переменная может стать очень большой.
  2. Циклическое перебор значения переменной без кавычек будет основываться на том, что оболочка разбивает данные на пробелы (, табуляции и символы новой строки ). Если данные содержат какие-либо пробелы, кроме символов новой строки, цикл, скорее всего, сделает что-то не так.
  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 <file

Этот скрипт будет принимать отметку времени в том же формате, что и в файле, в качестве единственного аргумента. Он перебирает содержимое файла fileи для каждой строки анализирует метку времени и сравнивает ее с меткой времени в командной строке. Сравнение выполняется с использованием оператора >в 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-го:-столбцов с разделителями из файла (отметки времени ), что означает, что нам не нужно выполнять какой-либо анализ исходных строк.

Связанные:

2
27.01.2020, 23:18

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

#!/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
0
27.01.2020, 23:18

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

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

Итак, собираем это вместе, без использования каких-либо сторонних инструментов и только с внутренними компонентами оболочки. Требуется команда dateиз GNU utils для использования флага -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

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

2
27.01.2020, 23:18

Если вы хотите повторять строки с for, вам нужно установить IFSна новую строку. Это будет немного быстрее, когда цикл while.

#!/bin/bash

IFS=$'\n'
for a in $(<file.txt); do
    [[ $1:ERR < ${a#*:*:} ]] && echo "$a"
done
$./script.sh 2018/07/16:12.35.10

(awk-версия)

#!/usr/bin/awk -bf

BEGIN { FS=OFS=":" } {
    if (d < $3 FS $4) { print $0 }
}
$./script.awk -vd=2018/07/16:12.35.10 file.txt

Если у вас уже есть дата, о которой вы знаете, что она существует, и вы просто хотите напечатать оставшиеся строки, вы можете отсортировать файл по дате, времени и использовать grep -A, чтобы получить контекст после соответствующей строки. tail +2позволяет начать вывод со второй строки, эффективно удаляя соответствующую строку из вывода.

$ grep < <(sort -t : -k 3,4 < file.txt) \
    -A2000 -Fe '2018/07/16:12.35.10' | tail +2 | sort -n
0
27.01.2020, 23:18

Ваши форматы дат сортируются лексически так же, как и хронологически (, по крайней мере, вCлокали ), поэтому здесь просто нужно выполнить сравнение строк:

#! /bin/sh -
# Usage: that-script <YYYY/MM/DD:HH.MM.SS> [<file> [<file>...]]

search_date=${1?Please specify the cut off date}
shift

LC_ALL=C exec awk -v min="$search_date" \
                  -v date_field=3 \
                  -v time_field=4 \
                  -F : -- '$date_field FS $time_field > min' "$@"

Здесь входные данные могут быть переданы либо на стандартный ввод, либо в виде аргументов файла, но имейте в виду, что awkрассматривает аргументы в формате var=valueкак назначения переменных, а -— как значения стандартного ввода, поэтому, если у вас есть файлы с такими именами, вы захотите добавить к именам файлов префикс ./.

Например, вызовите этот скрипт как

that-script 2018/07/16:12.36.15./my=file.txd

или

that-script 2018/07/16:12.36.15 < my=file.txt

С другой стороны, это означает, что вы также можете называть его как:

that-script 2018/07/16:12.36.15 date_field=5 time_field=6 file.txt

Для обработки некоторого ввода, где дата и время находятся в полях, отличных от 3 го и 4 го .

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

0
27.01.2020, 23:18

Теги

Похожие вопросы