解释

解释

简而言之,我的主要问题是在日期中打印记录一个字段与另一个字段相差不到一个月。所有日期都在月-日-年格式。

具体来说,我希望从包含 108 个竖线 ( |) 分隔字段的文件中提取记录,前提是它们满足以下条件:

  1. 日期字段 14 和 61 必须包含 10 月数据 #Resolved

  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< date -d '$14 1 month'.主要问题是 MM-DD-YYYY 格式,并且我正在比较两个字段。

输入(我没有标题,使用它们只是为了帮助解释我的示例数据。在大胆的不包括记录的原因):
.....|field14|field15|.....|Field61|.....
1.....|10-21-2016|11-23-2016|.....|2016年10月25日|.....
2.....|2016年10月21日|2016年11月20日|.....|11-25-2016|.....
3.....|10-21-2016|11-19-2016|.....|10-25-2016|.....
4... ..|2016年10月15日|2016年10月11日|.....|2016年10月25日|.....
5.....|2016年10月21日|10-19日-2016|.....|2016-10-25|.....
6.....|09-21-2016|09-19-2016|.....|10-25-2016|.....
所需输出(标题仅用于解释):
.....|field14|field15|.. ...|Field61|.....
3.....|2016年10月21日|2016年11月19日|.....|2016年10月25日|.....
4.. ...|2016年10月21日|2016年11月15日|.....|2016年10月25日|.....

我怎样才能解决这个问题?

答案1

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

解释

我们设置一个隐式行读取循环,并使用管道“|”分割读取的每一行建立分隔符和@F索引开始的数组。0

然后,我们将字段中的月/年/日信息填充1415标量变量中,以便于稍后在代码中进行操作。

当我们这样做时,我们会做一些轻微的调整,以确保m2y2d2日期始终比刚刚的日期更新m1y1d1,以便简化我们的日期逻辑计算。

最后,我们决定根据这 4 个标准来判断打印当前记录,即行,即:

  • 第 14 个元素,即$F[13]月份October日期。和
  • 第 61 个元素,即,$F[60]也是October月份日期。和
  • 两个日期相隔一个月,使用术语 时已考虑年份(y2-y1)*12,当较高日期的日期 < 较低日期的日期时,它们彼此相差一个月之内。或者
  • 这两个日期是同年同月 => 无论如何,它们都在一个月内。

答案2

为每一行运行date效率会非常低,您最好使用可以自行执行日期计算的文本处理工具,例如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 

答案3

实际上,如果你使用 ,这并不那么困难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;
    }
}

相关内容