【问题标题】:Change date format with awk使用 awk 更改日期格式
【发布时间】:2019-10-26 09:25:44
【问题描述】:

我有一个日志文件,我正在尝试使用 sed/awk/grep 重新格式化,但在日期格式方面遇到了困难。日志如下所示:

1.2.3.4 - - [28/Mar/2019:11:43:58 +0000] "GET /e9bb2dddd28b/5.6.7.8/YL0000000000.rom HTTP/1.1" "-" "Yealink W52P 25.81.0.10 00:00:00:00:00:00" 404 - 1 5 0.146

我想要这样的输出:

Yealink,1.2.3.4,28-03-2019 11:43:58

我尝试了以下方法:

grep Yealink access.log | grep 404 | sed 's/\[//g' | awk '{print "Yealink,",$1,",",strftime("%Y-%m-%d %H:%M:%S", $4)}' | sed 's/, /,/g' | sed 's/ ,/,/g'

编辑 - 在基于 cmets 将日期字符串传递给 strftime 之前删除 [ - 但仍无法按预期工作

但是这会返回一个空日期 - 很明显我的 strftime 语法错误:

Yealink,1.2.3.4,1970-01-01 01:00:00

【问题讨论】:

  • 据我所知,您将字符串[28/Mar/2019:11:43:58 传递给strftime,而不是unix 纪元时间。您是要先解析它还是要另辟蹊径?
  • 我已经删除了[,以便我以这种格式28/Mar/2019:12:05:11 传递一次strftime,但结果仍然相同 - 已编辑上面的帖子以反映这一点
  • 您可能需要以某种方式解析日期:grep 'Yealink.*404' access.log | perl -MTime::Piece -l -n -e 'next unless /^(\S+)\s[^[]+\[([^]]+)\]/; my $date = Time::Piece->strptime($2, "%d/%b/%Y:%H:%M:%S %z")->strftime("%F %T"); print "Yealink,$1,$date";'
  • @Score_Under 虽然我没想到会使用 Perl,但它仍然有效!感谢您解决这个问题。

标签: linux bash awk sed grep


【解决方案1】:

2019-10-25 更新:gawk 现在在扩展库中获取 strptime(),请参阅 https://groups.google.com/forum/#!msg/comp.lang.awk/Ft6_h7NEIaE/tmyxd94hEAAJ


原帖: 有关 strftime,请参阅the gawk manual,它不期望任何格式的时间,除了自纪元以来的秒数。如果 gawk 有一个 strptime() 那么它会起作用,但它不起作用(和I can't persuade the maintainers to provide one)所以你必须将时间戳转换为 mktime() 可以转换为秒的格式然后将其传递给 strftime(),例如:

$ awk '{
    split($4,t,/[[\/:]/)
    old  = t[4] " " (index("JanFebMarAprMayJunJulAugSepOctNovDec",t[3])+2)/3 " " t[2] " " t[5] " " t[6] " " t[7];
    secs = mktime(old)
    new  = strftime("%d-%m-%Y %T",secs);
    print $4 ORS old ORS secs ORS new
}' file
[28/Mar/2019:11:43:58
2019 3 28 11 43 58
1553791438
28-03-2019 11:43:58

当然,您根本不需要 mktime() 或 strftime() - 只需调整日期组件即可:

$ awk '{
    split($4,t,/[[\/:]/)
    new = sprintf("%02d-%02d-%04d %02d:%02d:%02d",t[2],(index("JanFebMarAprMayJunJulAugSepOctNovDec",t[3])+2)/3,t[4],t[5],t[6],t[7])
    print $4 ORS new
}' file
[28/Mar/2019:11:43:58
28-03-2019 11:43:58

这适用于任何 awk,而不仅仅是 GNU awk,因为它不需要时间函数。

index("JanFebMarAprMayJunJulAugSepOctNovDec",t[3])+2)/3 只是将 3 个字符的月份名称缩写(例如 Mar)转换为等效月份编号 (3) 的惯用方式。

【讨论】:

  • JanFebMarAprMayJunJulAugSepOctNovDec 不可移植,因为它不适用于不同的语言环境。使用时间函数会起作用。
  • @brunorey OP 在她的数据中包含这些月份名称,但没有其他月份名称。没有必要使程序复杂化或使它变得笨拙以尝试解决 OP 没有的问题。如果需要,可以为任何其他语言环境的月份名称编写类似的函数,但是是的,如果跨语言环境的可移植性是一个要求,那么你必须查看时间函数但要清楚 - 因为即使 gawk 也没有 strptime(),没有 awk 具有能够理解月份名称、缩写或其他名称的时间函数。
【解决方案2】:

另一个 awk,感谢 @EdMorton 审查 getline 的使用情况。

这里的想法是在 awk 中使用date 命令,它接受缩写的月份

$ date -d"28/Mar/2019:11:43:58 +0000" "+%F %T"  # Fails
date: invalid date ‘28/Mar/2019:11:43:58 +0000’
$ date -d"28 Mar 2019:11:43:58 +0000" "+%F %T"  # Again fails because of : before time section
date: invalid date ‘28 Mar 2019:11:43:58 +0000’
$ date -d"28 Mar 2019 11:43:58 +0000" "+%F %T" # date command works but incorrect time because of + in the zone
2019-03-28 17:13:58
$ date -d"28 Mar 2019 11:43:58" "+%F %T" # correct value after stripping +0000
2019-03-28 11:43:58
$

结果

awk -F"[][]" -v OFS=, '/Yealink/ { 
split($1,a," ");              #Format $1 to get IP
gsub("/", " ",$2); sub(":"," ",$2); sub("\\+[0-9]+","",$2); # Massage to get data value
cmd = "date -d\047" $2 "\047 \047+%F %T\047"; if ( (cmd | getline line) > 0 ) $2=line; close(cmd) # use system date
print "Yealink",a[1],$2 
} ' access.log

以下是文件内容

$ cat access.log
1.2.3.4 - - [28/Mar/2019:11:43:58 +0000] "GET /e9bb2dddd28b/5.6.7.8/YL0000000000.rom HTTP/1.1" "-" "Yealink W52P 25.81.0.10 00:00:00:00:00:00" 404 - 1 5 0.146
$

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-05-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-04
    • 2016-04-16
    • 2014-05-07
    • 2018-03-11
    相关资源
    最近更新 更多