2015-12-16 47 views
2

假设我有一个日志文件mylog这样的:如何用awk轻松过滤日志?

[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 

而且我想找到那些在18.00和10月1日之间发生的1.00 11月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[]中,以便可以访问它(有趣的是,glenn jackman的回答为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不提供更简单的方法来做到这一点?

+0

您好,我不熟悉awk或gawk,来到这里是因为正则表达式标签,并找到您的问题有趣。我熟悉.bat编程,在这种情况下,我们使用操作系统定义的变量来处理这种事情。是否有可能将环境变量与参数混合到awk中? –

+0

@JorgeCampos感谢您的评论。是的,在'awk'中你可以使用环境变量。例如,你可以说'awk -v myvar =“$ shell_var”'BEGIN {print myvar}''打印一个shell变量。请参阅'-v'的用法来传递它。 – fedorqui

+0

难道这不是您的问题的解决方案吗?当然,如果没有更好的办法。 –

回答

2

使用ISO 8601时间格式!

但是,这似乎是相当多的工作,应该是更直截了当的事情。

是的,这应该是直截了当的,其原因是因为日志不使用ISO 8601。应用程序日志应该使用ISO格式和UTC来显示时间,其他设置应该被视为中断和修复。

您的请求应该分为两部分。第一部分封为圣人的日志,将日期转换为ISO格式,第二个进行研究:

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); 
    myisodate=sprintf("%4d-%2d-%2dT%2d:%2d:%2d%6s", year,month,day,hour,min,sec,utc); 
$1 = myisodate 
print 
}' mylog 

约ISO 8601日期的好处 - 除了他们是一个标准 - 就是时间顺序一致字典顺序,因此,您可以使用/…/,/…/运算符来提取您感兴趣的日期。例如,要查找1月2015 18:00 +0200之间发生的事情2015年11月1日01:00 +0200,append以下为过滤器的标准化过滤器:

awk '/2015-10-01:18:00:00+0200/,/2015-11-01:01:00:00+0200/' 
+0

你能否回答我的这个问题http://stackoverflow.com/questions/39853960/parsing-lines-from-a-file-containing-date-time-greater-than-something。我有一个开放的赏金价值100 :) –

+0

我的日志文件中的日期格式有点不同。我试着从这个问题给出的日期格式开始,创建一个日志文件,内容与问题中给出的内容相同,并尝试像这样运行awk命令 - awk <你的命令><日志路径文件>',但我没有得到任何输出。 –

0

没有进入时间格式(假设所有记录的格式都一样),您可以使用sort | awk组合来轻松实现。

这里假设日志没有排序,根据你的格式和特殊排序选项来排序月份(M)和awk来选择感兴趣的范围。排序依据年份,月份和日期依次排列。

$ sort -k1.9,1.12 -k1.5,1.7M -k1.2,1.3 log | awk '/01\/Oct\/2015/,/01\/Nov\/2015/' 

如果文件已经排序,您可以轻松扩展以包含时间,并删除排序。

下有时间上的限制以及

awk -F: '/01\/Oct\/2015/ && $2>=18{p=1} 
     /01\/Nov\/2015/ && $2>=1 {p=0} p' 
+0

请注意,这比我在我的问题中使用的通用性更低,也非常具体。我的意思是,它的工作原理,我很感谢你的努力,但没有帮助推广这个问题,并提供了一个好的工具,以给定的格式和两个给定的日期时间过滤日志。 – fedorqui

+0

为什么需要使用两种不同的时间格式?如果您可以在日志中使用相同的格式,则脚本将变得微不足道。 – karakfa

0

我会用date命令里面awk实现这一目标,但不知道如何做到这一点有大的日志文件执行。

awk -F "[][]" -v start="$(date -d"1 Oct 2015 18:00 +0200" +"%s")" 
    -v end="$(date -d"1 Nov 2015 01:00 +0200" +"%s")" '{ 
     gsub(/\//,"-",$2);sub(/:/," ",$2); 
     cmd="date -d\""$2"\" +%s" ; 
     cmd|getline mytimestamp; 
     close(cmd); 
     if (start<=mytimestamp && mytimestamp<=end) print 
}' mylog