regex - 如何使用awk轻松过滤日志?

标签 regex date awk timestamp gawk

假设我有一个像这样的日志文件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[] 中,以便可以访问它(有趣的是,可以查看 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 中“时间函数”部分的介绍是

Since one of the primary uses of AWK programs is processing log files that contain time stamp information, gawk provides the following functions for obtaining time stamps and formatting them.

所以我想知道:有没有更好的方法来做到这一点?例如,如果格式不是 dd/Mmm/YYYY:HH:MM:ss 而是类似 dd Mmm YYYY HH:MM:ss 的格式怎么办?难道不能在外部提供匹配模式,而不必每次发生这种情况时都必须更改它吗?我真的必须使用 match() 然后处理该输出以提供 mktime() 吗? gawk 没有提供更简单的方法来做到这一点吗?

最佳答案

使用 ISO 8601 时间格式!

However, this seems to be quite a bit of work for something that should be more straight forward.

是的,这应该很简单,但事实并非如此,是因为日志不使用 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 日期的好处 - 除了它们是标准 - 是时间顺序与字典顺序一致,因此,您可以使用 /…/,/…/ 运算符来提取您感兴趣的日期。例如,查找 2015 年 10 月 1 日 18:00 +02002015 年 11 月 1 日 01:00 +0200 之间发生的事情 em>,将以下过滤器附加到先前的标准化过滤器:

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

关于regex - 如何使用awk轻松过滤日志?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34311140/

相关文章:

bash - 如何使用 awk 检测只有一个字段的行并将其与下一行一起打印?

ajax - 无法使用 jmeter 从响应中提取简单数据

visual-studio - 是否有正则表达式可以在一个句子中找到两个不同的词?

ios - 尝试将当前日期和日期参数设置为过去的日期

replace - 递归多行替换: changing copyright headers

awk - 使用 sed 命令将缩写字符串 (U.S.A.) 替换为 USA

javascript - 设置自定义 HTML5 有效性消息属性会忽略模式正则表达式

regex - 优化一个巨大的 "OR"模式

sql - 如何在 SQL 中计算该月的最后一天?

php - MySQL/PHP 确定一天位于哪一周以供日历使用