Печать записи, когда дата в одном поле находится в пределах месяца от другого поля (даты в MM-DD -ГГГГ)

Вкратце, моя основная проблема - это печать записи, когда дата в одно поле находится в пределах месяца от другого поля . Все даты указаны в формате ММ-ДД-ГГГГ .

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

  1. Поле даты 14 и 61 должно содержать данные за октябрь # Решено

  2. Поле даты 14 должно быть меньше поля даты 15 + 1 месяц (15 $ <14 $ + 1 месяц) # Не решено

Мой код:

awk -F'|' '{ if ($14 ~ /10-..-2016/ && $61 ~ /10-..-2016/ && $15< date -d '$14 1 month'  ) print $0}' <input >output

Не работает $ 15 . Основная проблема - это формат ММ-ДД-ГГГГ и то, что я сравниваю два поля.

Ввод (у меня нет заголовков, я использую их только для объяснения моих выборочных данных. Выделено жирным шрифтом причина исключения записи):
..... | field14 | field15 | ..... | Field61 | .....
1 ..... | 21.10.2016 | 11- 23 -2016 | ..... | 25.10.2016 | .....
2 ..... | 10-21-2016 | 11-20-2016 | ..... | 11 -25-2016 | .....
3 ..... | 21.10.2016 | 19.11.2016 | ..... | 25.10.2016 | .....
4 ..... | 15.10.2016 | 11.10.2016 | ..... | 25.10.2016 | .....
5 ..... | 21.10.2016 | 10-19 -2016 | ..... | 25.10.2016 | .....
6 ..... | 09 -21-2016 | 09-19-2016 | ..... | 25.10.2016 | .....
Желаемый результат (заголовки используются только для объяснения):
..... | field14 | field15 | ..... | Field61 | .....
3 ..... | 21.10.2016 | 19.11.2016 | ..... | 25.10.2016 | .....
4 ..... | 21.10.2016 | 15.11.2016 | ..... | 25.10.2016 | .....

Как это исправить?

0
29.03.2017, 22:18
3 ответа
perl -F'[|]' -lane '
   ($m2, $d2, $y2, $m1, $d1, $y1) = map { split /-/ } @F[14,13];

   ($m2, $d2, $y2, $m1, $d1, $y1) =
   ($m1, $d1, $y1, $m2, $d2, $y2) if !($y2 > $y1 or $m2 > $m1 or $d2 > $d1);

   print if
      2 == grep /^10-\d{2}-\d{4}$/, @F[13,60]
                and
      (((12*($y2-$y1)+$m2-$m1) == 1 && ($d2 < $d1))
                    ||
          ((12*($y2-$y1)+$m2-$m1) == 0))
' yourfile

Пояснение

Мы устанавливаем неявный цикл чтения строк и разделяем каждую прочитанную строку с помощью разделителя pipe '|' и строим массив @F, индекс которого начинается с 0.

Затем мы запихиваем информацию о месяце/годе/дне из полей 14 и 15 в скалярные переменные для удобства манипуляций далее в коде.

И пока мы здесь, мы сделаем небольшое изменение, чтобы убедиться, что дата m2y2d2 всегда новее, чем m1y1d1, чтобы упростить наши логические вычисления даты.

Наконец, мы принимаем решение о печати текущей записи, она же строка, судя по этим 4 критериям, а именно:

  • 14-й элемент, т.е. $F[13] - это дата октября месяца. И
  • 61-й элемент, т.е. $F[60] - это тоже октябрь месяца. И
  • Две даты разделены месяцем, год был учтен используя термин (y2-y1)*12, они находятся в пределах месяца друг от друга, когда день более высокой даты < дня более низкой даты. ИЛИ
  • Две даты относятся к одному году, одному месяцу => они находятся в пределах месяца в любом случае.
1
28.01.2020, 02:46

На самом деле, это не так уж сложно, если вы используете GNU awk, который имеет встроенные функции времени:

$2 ~ /^10/ && $5 ~ /^10/ {
    split($2, t, "-");
    t1 = mktime(t[3] " " t[1] " " t[2] " 0 0 0");
    split($3, t, "-");
    t2 = mktime(t[3] " " t[1] " " t[2] " 0 0 0");
    if (t2 >= t1 && t2 - t1 <= 30*24*3600) {
        print;
    }
}
0
28.01.2020, 02:46

Запуск даты для каждой строки был бы совершенно неэффективным, вам лучше использовать инструмент обработки текста, который может вычислять дату сам по себе, например perl :

perl -MTime::Piece -F'[|]' -lane 'print if
   $F[13] =~ /10-..-2016/ && 
   $F[60] =~ /10-..-2016/ &&
   Time::Piece->strptime($F[14], "%m-%d-%Y") <
     Time::Piece->strptime($F[13], "%m-%d-%Y")->add_months(1)' file 
0
28.01.2020, 02:46

Теги

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