Вкратце, моя основная проблема - это печать записи, когда дата в одно поле находится в пределах месяца от другого поля . Все даты указаны в формате ММ-ДД-ГГГГ .
В частности, я хочу извлечь записи из файла, который содержит 108 полей с разделителями ( |
), если они соответствуют следующим критериям:
Поле даты 14 и 61 должно содержать данные за октябрь # Решено
Поле даты 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 | .....
Как это исправить?
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 критериям, а именно:
$F[13]
- это дата октября
месяца. И$F[60]
- это тоже октябрь
месяца. И(y2-y1)*12
, они находятся в пределах месяца друг от друга, когда
день более высокой даты < дня более низкой даты. ИЛИНа самом деле, это не так уж сложно, если вы используете 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;
}
}
Запуск даты
для каждой строки был бы совершенно неэффективным, вам лучше использовать инструмент обработки текста, который может вычислять дату сам по себе, например 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