unix awk从日期字段

时间:2017-07-25 21:10:46

标签: shell date unix awk

我有一个大约有10MM记录的文件。这是我的dateSample src文件:

0000000566 2017/01/01 0
0000000055 2017/01/01 0
0000000109 2017/01/01 1
0000000940 2017/01/01 0
0000000566 2017/01/01 1
0000000055 2017/01/01 1
0000000109 2017/01/01 2

我基本上需要减去日期中月份的最后一个整数值,并打印没有整数的新值,因此:

0000000566 2017/01/01
0000000055 2017/01/01
0000000109 2016/12/01
0000000940 2017/01/01
0000000566 2016/12/01
0000000055 2016/12/01
0000000109 2016/11/01

我一直遇到日期问题(或测试时在macOS上使用gdate)并且在过去几天一直在徒劳地搜索。
它要么加前缀0,要么删除m和d值:

awk '{ print (gdate -d $2 +"%Y/%m/%d") }' <$src

或以零为后缀并减去年份中的整数:

awk '{ print (gdate -d $2 +-$3 months +"%Y/%m/%d") }' <$src

或将整个东西混合在一起仍然是不正确的:

awk '{ print gdate -d (gdate -d $2 +"%Y/%m/%d") +-$3 months +"%Y/%m/%d" }' <$src

我发现了以下优秀的回复:     Increment date with AWK for few days and months 这正是我想要的,但它运行得非常慢,我假设是因为命令中的命令。

这是当前的awk(我正在使用gdate,因为我现在正在运行macOS BSD):

awk '{ cmd=" gdate -d \"$(gdate -d \""$2"\")+\"-"$3"\"months\" \"+%Y/%m/%d\" ";
       cmd | getline fmtDate; close(cmd); 
       print $1, fmtDate
     }' <$src

所以我基本上需要以高效的方式输出 提前感谢任何指导/重写。
干杯

3 个答案:

答案 0 :(得分:2)

如果您的awk支持time functions mktimestrftime(这是GNU扩展程序),您可以这样做:

awk -F'[ /]' '{print $1 " " strftime("%Y/%m/%d", mktime($2" "($3-$5)" "$4" 0 0 0"))}' file

首先,我们将日期转换为Unix时间戳。 mktime仅以"YYYY MM DD HH MM SS"格式接受日期,这就是我们需要手动构建日期的原因。但它会自动执行规范化,并且会很快将"2017 -1 1 0 0 0"转换为与"2016 11 1 0 0 0"相同的时间戳。

之后我们只需要将时间戳转换为&#34; y / m / d&#34;格式并打印出来。

或者,您可以手动执行日期算术&#34;&#34;在简单的情况下,不需要日期标准化 - 如果该月的日期总是<= 28。 (对于28以上的日期,如31,您还需要在下面的脚本中添加剪辑/限制或溢出,但是您必须照顾闰年等。)< / p>

#!/usr/bin/awk -f

BEGIN {
    FS = "[ /]";
}

{
    mm = $2 * 12 + ($3 - 1) - $5;
    y = int(mm / 12);
    m = mm % 12 + 1;
    d = $4;
    printf("%s %04d/%02d/%02d\n", $1, y, m, d);
}

所以,这个想法很简单。我们将空格和斜线分开,因此我们可以将年/月转换为总月数(12 * y + m)。然后我们从最后一列中减去月份,并通过divmod操作将总月数转换回年/月。

输出:

$ ./script.awk file
0000000566 2017/01/01
0000000055 2017/01/01
0000000109 2016/12/01
0000000940 2017/01/01
0000000566 2016/12/01
0000000055 2016/12/01
0000000109 2016/11/01

答案 1 :(得分:1)

由于您正在操作日期,因此最好在shell中执行此操作:

while read -r str date n; do
   echo "$str $(date -d "$(date -d $date) -$n months" '+%Y/%m/%d')"
done < file

0000000566 2017/01/01
0000000055 2017/01/01
0000000109 2016/12/01
0000000940 2017/01/01
0000000566 2016/12/01
0000000055 2016/12/01
0000000109 2016/11/01

答案 2 :(得分:1)

尝试一种更简单的方法来解决这个问题。

awk 'BEGIN{
     split("01,02,03,04,05,06,07,08,09,10,11,12", month,",")
}
{
     split($2, array,"/");
       if(array[2]<=$3){
       array[2]=array[2]+12-$3;
       array[1]=array[1]-1
      }
       else{
       array[2]-$3
      };
     print $1,array[1]"/"array[2]"/"array[3]
}
'  Input_file