如何使用awk轻松过滤日志?
How to filter logs easily with awk?
假设我有一个这样的日志文件 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:00 和 11 月 1 日 1.00 之间的行。也就是说,预期输出为:
[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()
and then mktime()
. First one finds the specified pattern, that is stored in the array a[]
so it can be accessed (interesting to see glenn jackman's answer to access captured group from line pattern for a good example). Since mktime
requires a format YYYY MM DD HH MM SS[ DST]
, I also have to convert the month in the form Xxx
into a digit, for which I use an answer by Ed Morton to "convert month from Aaa to xx" 将时间转换为时间戳:awk '{printf "%02d\n",(match("JanFebMarAprMayJunJulAugSepOctNovDec",[=23=])+2)/3}'
。
总之,我终于在变量中有了时间戳 mytimestamp
:
awk 'match([=12=], /([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
Returns:
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([=15=], /([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
中引入 "Time functions" 部分是
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
没有提供更简单的方法吗?
无需进入时间格式(假设所有记录的格式相同),您可以使用 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/ && >=18{p=1}
/01\/Nov\/2015/ && >=1 {p=0} p'
我会在 awk
中使用 date
命令来实现这一点,尽管不知道这将如何处理大日志文件。
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(/\//,"-",);sub(/:/," ",);
cmd="date -d\"""\" +%s" ;
cmd|getline mytimestamp;
close(cmd);
if (start<=mytimestamp && mytimestamp<=end) print
}' mylog
使用 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([=10=], /([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);
= myisodate
print
}' mylog
ISO 8601 日期的好处 - 除了它们是 标准 - 时间顺序与字典顺序一致,因此,您可以使用 /…/,/…/
运算符提取您感兴趣的日期。例如,查找 1 Oct 2015 18:00 +0200 和 1 Nov 2015 [=30= 之间发生的事情] +0200,将以下过滤器附加到前面的标准化过滤器:
awk '/2015-10-01:18:00:00+0200/,/2015-11-01:01:00:00+0200/'
假设我有一个这样的日志文件 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:00 和 11 月 1 日 1.00 之间的行。也就是说,预期输出为:
[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()
and then mktime()
. First one finds the specified pattern, that is stored in the array a[]
so it can be accessed (interesting to see glenn jackman's answer to access captured group from line pattern for a good example). Since mktime
requires a format YYYY MM DD HH MM SS[ DST]
, I also have to convert the month in the form Xxx
into a digit, for which I use an answer by Ed Morton to "convert month from Aaa to xx" 将时间转换为时间戳:awk '{printf "%02d\n",(match("JanFebMarAprMayJunJulAugSepOctNovDec",[=23=])+2)/3}'
。
总之,我终于在变量中有了时间戳 mytimestamp
:
awk 'match([=12=], /([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
Returns:
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([=15=], /([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
中引入 "Time functions" 部分是
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
没有提供更简单的方法吗?
无需进入时间格式(假设所有记录的格式相同),您可以使用 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/ && >=18{p=1}
/01\/Nov\/2015/ && >=1 {p=0} p'
我会在 awk
中使用 date
命令来实现这一点,尽管不知道这将如何处理大日志文件。
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(/\//,"-",);sub(/:/," ",);
cmd="date -d\"""\" +%s" ;
cmd|getline mytimestamp;
close(cmd);
if (start<=mytimestamp && mytimestamp<=end) print
}' mylog
使用 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([=10=], /([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);
= myisodate
print
}' mylog
ISO 8601 日期的好处 - 除了它们是 标准 - 时间顺序与字典顺序一致,因此,您可以使用 /…/,/…/
运算符提取您感兴趣的日期。例如,查找 1 Oct 2015 18:00 +0200 和 1 Nov 2015 [=30= 之间发生的事情] +0200,将以下过滤器附加到前面的标准化过滤器:
awk '/2015-10-01:18:00:00+0200/,/2015-11-01:01:00:00+0200/'