我正在开发一个解析数百万行文本的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中调用这个命令,但我无法弄清楚怎么做。我想知道
提前致谢
答案 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