如何使用AWK格式化字符串日期(包含文本和毫秒)

时间:2017-03-11 17:00:53

标签: bash shell unix awk

我正在开发一个解析数百万行文本的AWK脚本。每行包含(除其他外)日期和时间。表格上的时间:

16-FEB-2008 14:17:59.994669

我需要将其转换为以下形式

20080216141759994669000

如果可能的话,我希望避免将月份从文本手动转换为数值。在bash中,我可以简单地执行以下命令来获得所需的结果:

date -d "16-FEB-2008 14:17:59.994669" +"%Y%m%d%H%M%S%N"

我试过在AWK中调用这个命令,但我无法弄清楚怎么做。我想知道

  1. 单独使用AWK是否可以实现?
  2. 如何在AWK脚本文件中使用这样的命令?
  3. 提前致谢

5 个答案:

答案 0 :(得分:5)

将月份名称转换为awk中的数字很容易,重新格式化也是如此,只要您不需要(额外)验证date做免费&#39;:< / p>

$ echo this 16-FEB-2008 14:17:59.994669 that \
> | awk '{ split($2,d,"-"); split($3,t,"[:.]"); 
    m=sprintf("%02d",index("JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC",d[2])/4+1);
    print $1,d[3] m d[1] t[1] t[2] t[3] t[4] "000",$4 }'
this 20080216141759994669000 that
$ # or can put the script in a file and use with awk -f
$ # or the whole thing in a shebang file like #!/bin/awk -f

这不比运行date的代码长得多,而且对于数百万行来说效率更高。

答案 1 :(得分:4)

您可以像这样调用外部命令:

awk '{
         cmd="date -d \""$0"\" +%Y%m%d%H%M%S%N"
         cmd | getline ts
         print $0, ts
         # awk opened a pipe for the communication with 
         # the command. close that pipe to avoid running
         # out of file descriptors
         close(cmd)
     }' <<< '16-FEB-2008 14:17:59.994669'

输出:

16-FEB-2008 14:17:59.994669 20080216141759994669000

感谢dave_thompson_085的评论,如果你有来自GNU coreutils和date的{​​{1}},你可以大大提高性能。 GNU的gawk支持从stdin读取日期,date支持协同进程,允许在后台启动单个gawk实例,写入stdin并从stdout读取:

date

请注意,除了强制{ cmd = "stdbuf -oL date -f /dev/stdin +%Y%m%d%H%M%S%N" print $0 |& cmd cmd |& getline ts print $0, ts } 之外,还需要使用stdbuf命令逐行输出结果。

答案 2 :(得分:2)

使用perl:

LANG=C perl -MTime::Piece -plE 's/\b(\d+-\w{3}-\d{4}\s+\d+:\d+:\d+)\.(\d+)\b/Time::Piece->strptime($1,q{%d-%b-%Y %H:%M:%S})->strftime(q{%Y%m%d%H%M%S}).$2/ge' < in >out

使用重新格式化(并验证)的日期全局替换每个类似时间的模式。

核心模块Time::Piece不支持小数秒,所以解决方案有点黑客......

答案 3 :(得分:2)

这里有很多好的答案。这是一个使用awk 帮助函数来重新格式化日期的人。

awk '
  BEGIN { 
    mi["JAN"]="01"; mi["FEB"]="02"; mi["MAR"]="03"; mi["APR"]="04"; mi["MAY"]="05"; mi["JUN"]="06"
    mi["JUL"]="07"; mi["AUG"]="08"; mi["SEP"]="09"; mi["OCT"]="10"; mi["NOV"]="11"; mi["DEC"]="12"
  }
  function reformatDate(dtStr, tmStr) {
    split(dtStr, dtParts, "-"); gsub(/[:.]/, "", tmStr)
    return dtParts[3] mi[dtParts[2]] sprintf("%02d", dtParts[1]) tmStr "000"
  }
  { print reformatDate($1, $2) }
' <<<'16-FEB-2008 14:17:59.994669'

答案 4 :(得分:1)

这里没有必要调用日期,你只需要一个月的查找

$ awk -F'[- :.]' -v OFS='' '
     BEGIN {split("JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC",m);
            for(i=1;i<=12;i++) a[m[i]]=i<10?"0"i:i}
           {$2=a[$2]; y=$3; $3=$1; $1=y; print $0 "000"}' file