Временная арифметика в bash - PullRequest
4 голосов
/ 17 января 2012

Скажем, у меня есть два файла журнала (input.log и output.log) в следующем формате:

2012-01-16T12:00:00 12345678

Первое поле - это отметка времени обработки, а второе - уникальный идентификатор. Я пытаюсь найти:

  1. Записи из input.log, которые не имеют соответствующей записи для этого идентификатора в output.log
  2. Записи из input.log, которые имеют запись для этого идентификатора, , но разница во временных отметках превышает 5 секунд

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

У меня есть следующее, которое возвращает строки input.log с добавленным столбцом, если output.log содержит идентификатор:

join -a1 -j2 -o 0 1.1 2.1 <(sort -k2,2 input.log) <(sort -k2,2 output.log)

Пример вывода:

10111 2012-01-16T10:00:00 2012-01-16T10:00:04
11562 2012-01-16T11:00:00 2012-01-16T11:00:10
97554 2012-01-16T09:00:00

Основной вопрос :

Теперь, когда у меня есть эта информация, как я могу вычислить разницу между двумя временными метками и отбросить их с интервалом более 5 секунд? Я столкнулся с некоторыми проблемами при обработке метки времени ISO 8601 с date (в частности, T) и предположил, что должен быть лучший способ.

Дополнительный вопрос :

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

Ответы [ 3 ]

4 голосов
/ 17 января 2012

Если у вас есть GNU awk, то вы можете попробовать что-то вроде этого -

gawk '
NR==FNR{a[$2]=$1;next} 
!($2 in a) {print $2,$1; next} 
($2 in a) {
  "date +%s -d " $1 | getline var1;
  "date +%s -d " a[$2] | getline var2;
  var3 = var2 - var1;
  if (var3 > 4) print $2, $1, a[$2]
}' output.log input.log

Тест:

[jaypal:~/Temp] cat input.log 
2012-01-16T09:00:00 9
2012-01-16T10:00:00 10
2012-01-16T11:00:00 11

[jaypal:~/Temp] cat output.log 
2012-01-16T10:00:04 10
2012-01-16T11:00:10 11
2012-01-16T12:00:00 12

[jaypal:~/Temp] gawk '
NR==FNR{a[$2]=$1;next} 
!($2 in a) {print $2,$1; next} 
($2 in a) {"date +%s -d " $1 | getline var1; "date +%s -d " a[$2] | getline var2;var3=var2-var1;if (var3>4) print $2,$1,a[$2] }' output.log input.log
9 2012-01-16T09:00:00
11 2012-01-16T11:00:00 2012-01-16T11:00:10

Пояснение:

  • NR==FNR{a[$2]=$1;next}

Мы начнем с сохранения первого поля в файле output.log в массиве, проиндексированном во втором поле. Мы используем next для предотвращения запуска других операторов pattern{action}. Использование NR==FNR позволяет нам полностью удалить файл output.log.

  • !($2 in a) {print $2,$1; next}

Как только файл output.log будет завершен. Начнем с файла input.log. Мы проверяем, нет ли второго поля, присутствующего в файле input.log, в нашем массиве (т.е. в файле output.log) Если найдено, мы распечатаем его. Мы продолжаем это действие, пока не распечатаем все эти поля.

  • ($2 in a) {"date +%s -d " $1 | getline var1; "date +%s -d " a[$2] | getline var2; var3=var2-var1; if (var3 > 4) print $2,$1,a[$2] }

Здесь мы ищем поля, которые присутствуют в обоих файлах. Когда мы находим эти поля, нам нужно включить нашу логику для вычисления разницы. Мы используем системную команду, чтобы найти дату. Теперь системная команда по умолчанию печатает в STDOUT, и мы не можем их контролировать. Таким образом, мы передаем вывод по каналу и фиксируем вывод, используя функцию awk getline, и сохраняем его в переменной (var1 и var2). Как только обе даты сохранены в переменной, мы делаем различие и сохраняем в var3, если оказалось, что var3> 4, мы печатаем его в формате, который вы хотите.

2 голосов
/ 17 января 2012

Вот решение, которое я выбрал:

cat input.log
2012-01-16T09:00:00 9
2012-01-16T10:00:00 10
2012-01-16T11:00:00 11

cat output.log
2012-01-16T10:00:04 10
2012-01-16T11:00:10 11
2012-01-16T12:00:00 12

sort -k2,2 input.log > input.sort
sort -k2,2 output.log > output.sort

join -a1 -j2 -o 0 1.1 2.1 input.sort output.sort | while read id i o; do
    if [ -n "$o" ]; then
        ot=$(date +%s -d "${o/T/ }")
        it=$(date +%s -d "${i/T/ }")
        [[ $it+5 -lt $ot ]] && echo $id $i $o
    else echo $id $i
    fi
done
11 2012-01-16T11:00:00 2012-01-16T11:00:10
9 2012-01-16T09:00:00
0 голосов
/ 17 января 2012
t1=2012-01-16T10:00:00
t2=2012-01-16T10:00:04
echo $(($(date -d $t1 +%s)-$(date -d $t2 +%s)))
-4
...