我目前正在拼凑一个工具来处理网络中生成的系统日志,其中一个要求是将日期时间从系统日志中的格式(%b %d %Y %T)转换为纪元。本质上,这就是我想要实现的目标:
原始系统日志格式:
1: Jul 02 2019 15:14:19: %ASA-6-106015: <message>
2: Jul 02 2019 15:14:49: %ASA-6-106015: <message>
最终日志:
1: 1562080489 %ASA-6-106015 <message>
2: 1562080529 %ASA-6-106015 <message>
我知道我可以通过迭代整个日志并执行 date -d 操作来做到这一点。这是我想避免的事情。我更喜欢使用 GAWK 时间函数。
这是我的方法,
gawk -F: '{ print strftime("%s", timestamp}' syslog.log
但这里的时间戳必须与 systime() 函数返回的值格式相同。但事实并非如此。
另外,我无法使用 mktime() 函数将 syslog 时间戳转换为所需的格式,因为它仅接受特定格式的输入 [YYYY MM DD HH MM SS]
我觉得有一种方法可以做到这一点,但我缺少它。任何替代方法也将受到赞赏。
答案1
使用 GNU date
,您可以运行date
一次并让它从标准输入获取输入。使用gawk 的协进程功能每个都有一个实例awk
并date
处理所有日期:
% awk -v cmd='stdbuf -oL date +%s -f-' -F': ' 'BEGIN{OFS=FS} {print $2 |& cmd; cmd |& getline $2} 1' foo
1: 1562048059: %ASA-6-106015: <message>
2: 1562048089: %ASA-6-106015: <message>
注意date
的输出需要不缓冲(因此stdbuf -oL
),否则协进程将挂起。
答案2
就像该date(1)
实用程序一样,gawk
smktime()
假设日期规范使用当地时间。
要强制它使用UTC
,TZ
应使用 envvar:
$ TZ=UTC gawk -F'[: ]+' '{sub(/([^:]+:){4} */, mktime(sprintf("%s %02d %s %d %d %d", $3, index(" JanFebMarAprMayJunJulAugSepOctNovDec",$1)/3, $2, $4, $5, $6))"\t"$7"\t"); print}'
1562080459 %ASA-6-106015 <message>
1562080489 %ASA-6-106015 <message>
答案3
以下是使用关联数组将月份名称转换为数字的典型方法,其中索引是月份名称,值是月份编号。例如mon["Jul"]
是 7。这是在 BEGIN 块中设置一次。
awk 'BEGIN {
split("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec",months," ")
for(i=1;i<=12;i++)mon[months[i]] = i }
{ m = $2; d = $3; y = $4; t = $5; gsub(":"," ",t)
print mktime(y " " mon[m] " " d " " t) }'
然后,对于每一行,各个字段被重新排列成正确的顺序,mktime()
并与中间的空格连接起来。时间t
字段已:
转换为空间字段。上面只是打印纪元时间,您仍然需要添加其余数据。
答案4
也许是珀尔:
perl -MTime::Piece -i.bak -pe '
if ( /([[:upper:]][[:lower:]]{2} \d{2} \d{4} \d\d:\d\d:\d\d)/ ) {
$datetime = Time::Piece->strptime($1, "%b %d %Y %T");
$epoch = $datetime->epoch;
s/$timestamp/$epoch/
}
' log_file