【发布时间】:2016-03-22 12:53:42
【问题描述】:
假设我有一个这样的日志文件mylog:
[01/Oct/2015:16:12:56 +0200] error number 1
[01/Oct/2015:17:12:56 +0200] error number 2
[01/Oct/2015:18:07:56 +0200] error number 3
[01/Oct/2015:18:12:56 +0200] error number 4
[02/Oct/2015:16:12:56 +0200] error number 5
[10/Oct/2015:16:12:58 +0200] error number 6
[10/Oct/2015:16:13:00 +0200] error number 7
[01/Nov/2015:00:10:00 +0200] error number 8
[01/Nov/2015:01:02:00 +0200] error number 9
[01/Jan/2016:01:02:00 +0200] error number 10
我想找出 10 月 1 日 18 点到 11 月 1 日 1 点之间出现的那些行。也就是说,预期的输出是:
[01/Oct/2015:18:07:56 +0200] error number 3
[01/Oct/2015:18:12:56 +0200] error number 4
[02/Oct/2015:16:12:56 +0200] error number 5
[10/Oct/2015:16:12:58 +0200] error number 6
[10/Oct/2015:16:13:00 +0200] error number 7
[01/Nov/2015:00:10:00 +0200] error number 8
我已经设法通过使用match() 和mktime() 将时间转换为时间戳。第一个找到指定的模式,该模式存储在数组a[] 中,因此可以访问(有趣的是,看看格伦杰克曼对access captured group from line pattern 的回答是一个很好的例子)。由于mktime 需要YYYY MM DD HH MM SS[ DST] 格式,我还必须将Xxx 格式的月份转换为数字,为此我使用an answer by Ed Morton to "convert month from Aaa to xx":awk '{printf "%02d\n",(match("JanFebMarAprMayJunJulAugSepOctNovDec",$0)+2)/3}'。
一起,最后我在变量mytimestamp中得到了时间戳:
awk 'match($0, /([0-9]+)\/([A-Z][a-z]{2})\/([0-9]{4}):([0-9]{1,2}):([0-9]{1,2}):([0-9]{1,2}) ([+-][0-9]{4})/, a) {
day=a[1]; month=a[2]; year=a[3];
hour=a[4]; min=a[5]; sec=a[6]; utc=a[7];
month=sprintf("%02d",(match("JanFebMarAprMayJunJulAugSepOctNovDec",month)+2)/3);
mydate=sprintf("%s %s %s %s %s %s %s", year,month,day,hour,min,sec,utc);
mytimestamp=mktime(mydate)
print mytimestamp
}' mylog
返回:
1443708776
1443712376
1443715676
等等
所以现在我已准备好根据给定日期进行转换。由于awk 处理这种格式需要很多时间,我更喜欢通过外部shell 变量提供它们,使用date -d"my date" +"%s" 打印时间戳:
start="$(date -d"1 Oct 2015 18:00 +0200" +"%s")"
end="$(date -d"1 Nov 2015 01:00 +0200" +"%s")"
总的来说,这是可行的:
awk start="$(date -d"1 Oct 2015 18:00 +0200" +"%s")" end="$(date -d"1 Nov 2015 01:00 +0200" +"%s")" 'match($0, /([0-9]+)\/([A-Z][a-z]{2})\/([0-9]{4}):([0-9]{1,2}):([0-9]{1,2}):([0-9]{1,2}) ([+-][0-9]{4})/, a) {day=a[1]; month=a[2]; year=a[3]; hour=a[4]; min=a[5]; sec=a[6]; utc=a[7]; month=sprintf("%02d",(match("JanFebMarAprMayJunJulAugSepOctNovDec",month)+2)/3); mydate=sprintf("%s %s %s %s %s %s %s", year,month,day,hour,min,sec,utc); mytimestamp=mktime(mydate); if (start<=mytimestamp && mytimestamp<=end) print}' mylog
[01/Oct/2015:18:07:56 +0200] error number 3
[01/Oct/2015:18:12:56 +0200] error number 4
[02/Oct/2015:16:12:56 +0200] error number 5
[10/Oct/2015:16:12:58 +0200] error number 6
[10/Oct/2015:16:13:00 +0200] error number 7
[01/Nov/2015:00:10:00 +0200] error number 8
但是,对于应该更直截了当的事情来说,这似乎是一项相当多的工作。尽管如此,man gawk 中“时间函数”部分的介绍是
由于 AWK 程序的主要用途之一是处理日志文件 包含时间戳信息,gawk 提供以下 获取时间戳和格式化它们的函数。
所以我想知道:有没有更好的方法来做到这一点?例如,如果格式而不是 dd/Mmm/YYYY:HH:MM:ss 是 dd Mmm YYYY HH:MM:ss 会怎样?难道不能在外部提供匹配模式,而不是每次发生这种情况时都必须更改它吗?我真的必须使用match(),然后处理该输出以提供mktime()吗? gawk 不是提供了更简单的方法吗?
【问题讨论】:
-
您好,我不熟悉 awk 或 gawk,来到这里是因为 regex 标记并发现您的问题很有趣。虽然我熟悉 .bat 编程,但在这种情况下,我们使用操作系统定义的变量来处理这类事情。是否可以将环境变量与 awk 的参数混合在一起?
-
@JorgeCampos 感谢您的评论。是的,在
awk中,您可以使用环境变量。例如你可以说awk -v myvar="$shell_var" 'BEGIN{print myvar}'来打印一个shell 变量。看-v的用法就可以通过了。 -
这不是您的问题的解决方案吗?当然,如果没有更好的方法。
-
@JorgeCampos mmm 是的,这实际上是我的问题之一:我可以在
match()函数外部提供这样的日期格式参数吗? -
根据文档,不,你不能。我看到的唯一方法是您使用外部变量。但正如我所说,我不是 awk 专家。也许其他人知道方法!
标签: regex date awk timestamp gawk