awk筛选给定范围内的日志
为什么要筛选
在服务器被攻击时,我们作为安全人员需要找到对方留下的蛛丝马迹,显而易见查看日志是一个很好的途径。但是日志有着成千上万条,为了方便我们进行查看,所以要进行给定范围内的日志筛选
为什么选择AWK
grep/sed/awk用正则去筛选日志时,如果要精确到小时、分钟、秒,则非常难以实现。主要难点在于日期时间各个部分的进制是不一样的,比如小时是24进制/12进制,分钟是60进制,秒也是60进制(闰秒时为61进制)。
但是awk提供了mktime()函数,它可以将时间转换成epoch时间值。
借此,可以取得日志中的时间字符串部分,再将它们的年、月、日、时、分、秒都取出来,然后放入mktime()构建成对应的epoch值。因为epoch值是数值,所以可以比较大小,从而决定时间的大小。
AWK操作
下面strptime1()实现的是将2019-11-10T03:42:40+08:00
格式的字符串转换成epoch值,然后和which_time比较大小即可筛选出精确到秒的日志。
建立过滤文件
BEGIN{
# 要筛选什么时间的日志,将其时间构建成epoch值
which_time = mktime("2019 11 10 03 42 40")
}
{
# 取出日志中的日期时间字符串部分
match($0,"^.*\\[(.*)\\].*",arr)
# 将日期时间字符串转换为epoch值
tmp_time = strptime1(arr[1])
# 通过比较epoch值来比较时间大小
if(tmp_time > which_time){print}
}
# 构建的时间字符串格式为:"2019-11-10T03:42:40+08:00"
function strptime1(str ,arr,Y,M,D,H,m,S) {
patsplit(str,arr,"[0-9]{1,4}")
Y=arr[1]
M=arr[2]
D=arr[3]
H=arr[4]
m=arr[5]
S=arr[6]
return mktime(sprintf("%s %s %s %s %s %s",Y,M,D,H,m,S))
}
下面strptime2()实现的是将10/Nov/2019:23:53:44+08:00
格式的字符串转换成epoch值,然后和which_time比较大小即可筛选出精确到秒的日志。
{
match($0,"^.*\\[(.*)\\].*",arr)
tmp_time = strptime2(arr[1])
if(tmp_time > which_time){
print
}
}
# 构建的时间字符串格式为:"10/Nov/2019:23:53:44+08:00"
function strptime2(str ,dt_str,arr,Y,M,D,H,m,S) {
dt_str = gensub("[/:+]"," ","g",str)
# dt_sr = "10 Nov 2019 23 53 44 08 00"
split(dt_str,arr," ")
Y=arr[3]
M=mon_map(arr[2])
D=arr[1]
H=arr[4]
m=arr[5]
S=arr[6]
return mktime(sprintf("%s %s %s %s %s %s",Y,M,D,H,m,S))
}
function mon_map(str ,mons){
mons["Jan"]=1; mons["Feb"]=2; mons["Mar"]=3
mons["Apr"]=4; mons["May"]=5; mons["Jun"]=6
mons["Jul"]=7; mons["Aug"]=8; mons["Sep"]=9
mons["Oct"]=10; mons["Nov"]=11; mons["Dec"]=12
return mons[str]
}
# mon_map函数可以简化成如下等价的定义方式:
function mon_map(mon, str) {
str = "JanFebMarAprMayJunJulAugSepOctNovDec"
return ( ( ( index(str, mon) - 1 ) / 3 ) + 1)
}
AWK过滤实例操作
准备日志文件
选择Apache
在windows D: /phpstuy_pro/Extensions/Apache2.4.39/logs access.log文件
编辑awk过滤
AWK查看文件
过滤
补充
awk 时间函数
在 awk 中,可以使用内置的函数 mktime
将时间转换为秒数,进而可以进行时间的比较。具体使用方法如下:
假设我们有一个文件 test.txt
,内容如下:如果我们想找到在 2022 年 2 月 1 日之前的所有如果我们想找到在 2022 年 2 月 1 日之前的所有行,可以使用以下命令:
2022-01-01 12:00:00
2022-02-01 12:00:00
2022-03-01 12:00:00
如果我们想找到在2022年2月1日之前所有的行,可以使用一下命令:
awk -F'[-: ]' '{if (mktime($1" "$2" "$3" "$4" "$5" "$6) < mktime("2022 02 01 00 00 00")) print $0}' test.txt
其中:
-F'[-: ]'
表示使用 -、:、空格 作为分隔符,将每行按照日期和时间分割成多个字段。
$1" "$2" "$3" "$4" "$5" "$6
表示将每行的前六个字段拼接成一个字符串,作为 mktime 函数的参数。
"2022 02 01 00 00 00"
表示将日期和时间转换为秒数。
if (mktime(...) < mktime("2022 02 01 00 00 00"))
表示比较时间,判断当前行的时间是否在 2022 年 2 月 1 日之前,如果是,则输出当前行。
以上命令的输出结果为:
2022-01-01 12:00:00
awk 时间类内置函数
awk 常用于处理日志,它支持简单的时间类操作。有下面 3 个内置的时间函数:
mktime("YYYY MM DD HH mm SS [DST]")
构建一个时间,返回这个时间点的秒级 epoch,构建失败则返回 - 1
systime()
返回当前系统时间点,返回的是秒级 epoch 值
strftime([format [, timestamp [, utc-flag] ] ])
将时间按指定格式转换为字符串并返回转的结果字符串
注意,awk构建时间都是返回秒级的epoch值,表示从1970-01-01 00:00:00 开始到指定时间已经过的秒数
awk mktime()
mktime 在构建时间时,如果传递的 DD 给定的值超出了月份 MM 允许的天数,则自动延申到下个月。例如,指定”2022 2 29 12 30 59” 中 2 月只有 28 号,所以构建出来的时间是 2022-03-01 12:30:59。
此外,其它部分也不限定必须在范围内。例如,2022 2 23 12 32 65 的秒超出了 59,那么多出来的秒数将进位到分钟。
awk 'BEGIN{
print mktime("2019 2 23 12 32 65") | "xargs -i date -d@{} +\"%F %T\""
}'
2019-02-23 12:33:05
如果某部位的数值为负数,则表示在此时间点基础上减几。例如:
# 2022-02-23 12:00:59基础上减1分钟
$ awk 'BEGIN{print mktime("2019 2 23 12 -1 59") | "xargs -i date -d@{} +\"%F %T\""}'
2019-02-23 11:59:59
# 2022-02-23 00:32:59基础上减1小时
$ awk 'BEGIN{print mktime("2019 2 23 -1 32 59") | "xargs -i date -d@{} +\"%F %T\""}'
2019-02-22 23:32:59
awk strftime()
strftime([format [, timestamp [, utc-flag] ] ])
将指定的时间戳 tiemstamp 按照给定格式 format 转换为字符串并返回这个字符串。
如果省略 timestamp,则对当前系统时间进行格式化。
如果省略 format,则采用 PROCINFO [“strftime”] 的格式,其默认格式为 %a %b %e %H:%M%:S %Z %Y。该格式对应于 Shell 命令 date 的默认输出结果。
$ awk 'BEGIN{print strftime()}'
Wed Oct 30 00:20:01 CST 2014
$ date
Wed Oct 30 00:20:04 CST 2014
$ awk 'BEGIN{print strftime(PROCINFO["strftime"], systime())}'
Wed Oct 30 00:24:00 CST 2014
支持的格式包括:
%a 星期几的缩写,如Mon、Sun Wed Fri
%A 星期几的英文全名,如Monday
%b 月份的英文缩写,如Oct、Sep
%B 月份的英文全名,如February、October
%C 2位数的世纪,例如1970对应的世纪是19
%y 2位数的年份(00–99),通过年份模以100取得,例如2019/100的余数位19
%Y 四位数年份(如2015)
%m 月份(01–12)
%j 年中天(001–366)
%d 月中天(01–31)
%e 空格填充的月中天
%H 24小时制的小时(00–23)
%I 12小时制的小时(01–12)
%p 12小时制时的AM/PM
%M 分钟数(00–59)
%S 秒数(00–60)
%u 数值的星期几(1–7),1表示星期一
%w 数值的星期几(0–6),0表示星期日
%W 年中第几周(00–53)
%z 时区偏移,格式为"+HHMM",如"+0800"
%Z 时区偏移的英文缩写,如CST
%k 24小时制的时间(0-23),1位数的小时使用空格填充
%l 12小时制的时间(1-12),1位数的小时使用空格填充
%s 秒级epoch
##### 特殊符号
%n 换行符
%t 制表符
%% 百分号%
##### 等价写法:
%x 等价于"%A %B %d %Y"
%F 等价于"%Y-%m-%d",用于表示ISO 8601日期格式
%T 等价于"%H:%M:%S"
%X 等价于"%T"
%r 12小时制的时间部分格式,等价于"%I:%M:%S %p"
%R 等价于"%H:%M"
%c 等价于"%A %B %d %T %Y",如Wed 30 Oct 2015 12:34:48 AM CST
%D 等价于"%m/%d/%y"
%h 等价于"%b"
例如
$ awk 'BEGIN{print strftime("%s", mktime("2077 11 12 10 23 32"))}'
3403909412
$ awk 'BEGIN{print strftime("%F %T %Z", mktime("2077 11 12 10 23 32"))}'
2077-11-12 10:23:32 CST
$ awk 'BEGIN{print strftime("%F %T %z", mktime("2077 11 12 10 23 32"))}'
2077-11-12 10:23:32 +0800
awk 将日期时间字符串转换为时间:strptime1 ()
例如:
2019-11-11T03:42:42+08:00
1. 将日期时间字符串中的年月日时分秒全都单独保存起来
2. 将年月日时分秒构建成 mktime () 的字符串格式”YYYY MM DD HH mm SS”
3. 使用 mktime () 可以构建出时间点
function strptime(str, time_str,arr,Y,M,D,H,m,S){
time_str = gensub(/[-T:+]/," ","g",str)
split(time_str, arr, " ")
Y = arr[1]
M = arr[2]
D = arr[3]
H = arr[4]
m = arr[5]
S = arr[6]
# mktime失败返回-1
return mktime(sprintf("%d %d %d %d %d %d", Y,M,D,H,m,S))
}
BEGIN{
str = "2019-11-11T03:42:42+08:00"
print strptime(str)
}
awk 将日期时间字符串转换为时间:strptime2 ()
下面是更难一点的,月份使用的是英文或英文缩写,日期时间分隔符也比较特殊。
Sat 26. Jan 15:36:24 CET 2013
function strptime(str, time_str,arr,Y,M,D,H,m,S){
time_str = gensub(/[.:]+/, " ", "g", str)
split(time_str, arr, " ")
Y = arr[8]
M = month_map(arr[3])
D = arr[2]
H = arr[4]
m = arr[5]
S = arr[6]
return mktime(sprintf("%d %d %d %d %d %d", Y,M,D,H,m,S))
}
function month_map(str, mon){
# mon = substr(str,1,3)
# return (((index("JanFebMarAprMayJunJelAugSepOctNovDec", mon)-1)/3)+1)
mon["Jan"] = 1
mon["Feb"] = 2
mon["Mar"] = 3
mon["Apr"] = 4
mon["May"] = 5
mon["Jun"] = 6
mon["Jul"] = 7
mon["Aug"] = 8
mon["Sep"] = 9
mon["Oct"] = 10
mon["Nov"] = 11
mon["Dec"] = 12
return mon[str]
}
BEGIN{
str = "Sat 26. Jan 15:36:24 CET 2013"
print strptime(str)
}