我正在尝试使用特定文本过滤大于当前日期的特定时间的日志。我已成功过滤日志中包含当前日期的文本。这是命令:
grep "$(date +"%d/%b/%Y")" test.log | grep -i "failed login"
这是示例日志:
[04/Dec/2019 02:05:13 -0800] access WARNING 10.126.49.92 -anon- - "POST /hue/accounts/login HTTP/1.1"-- Failed login for user: testuser
[04/Dec/2019 02:05:15 -0800] access WARNING 10.126.49.92 -anon- - "POST /hue/accounts/login HTTP/1.1"-- Failed login for user: testuser
[04/Dec/2019 02:04:59 -0800] access INFO 10.126.49.92 ahmed.rao - "POST /notebook/api/check_status HTTP/1.1" returned in 759ms
[04/Dec/2019 02:05:00 -0800] base INFO Selected cluster 0e83a448-26c9-459b-a0f2-3478ecb119af {u'interface': u'impala', u'namespace': u'0e83a448-26c9-459b-a0f2-3478ecb119af', u'type': u'direct', u'id': u'0e83a448-26c9-459b-a0f2-3478ecb119af', u'name': u'0e83a448-26c9-459b-a0f2-3478ecb119af'} interface hiveserver2
[04/Dec/2019 03:05:00 -0800] access INFO 10.126.49.92 ahmed.rao - "POST /notebook/api/close_statement HTTP/1.1" returned in 1345ms
[04/Dec/2019 03:05:00 -0800] base INFO Selected cluster 0e83a448-26c9-459b-a0f2-3478ecb119af {u'interface': u'impala', u'namespace': u'0e83a448-26c9-459b-a0f2-3478ecb119af', u'type': u'direct', u'id': u'0e83a448-26c9-459b-a0f2-3478ecb119af', u'name': u'0e83a448-26c9-459b-a0f2-3478ecb119af'} interface hiveserver2
[04/Dec/2019 03:05:18 -0800] access WARNING 10.126.49.92 -anon- - "POST /hue/accounts/login HTTP/1.1"-- Failed login for user: testuser
但是,我不知道如何强制执行大于特定时间的条件。
答案1
使用ts
from moreutils
,您可以轻松地将这些时间戳转换为更有用的格式:
ts -r %FT%T%z < file.log |
awk '$0 > "[2019-12-04T02:50" && tolower($0) ~ /failed login/'
根据您的输入(以及时区America/Los_Angeles
)给出:
[2019-12-04T03:05:18-0800] access WARNING 10.126.49.92 -anon- - "POST /hue/accounts/login HTTP/1.1"-- Failed login for user: testuser
ts
with-r
解析该时间戳并将其转换为指定的%FT%T%z
strftime
格式(在您的时区中)。
由于该YYYY-MM-DDTHH:MM:SS
格式按词法和时间顺序排序相同,因此只需进行字符串比较即可awk
查找晚于指定日期的条目。awk
还可以做grep -i
的工作。这里使用tolower()
不区分大小写匹配的标准方法。使用 GNUawk
你还可以这样做:
gawk -v IGNORECASE=1 '$0 > "[2019-12-04T02:50" && /failed login/'
如果您没有,您可以使用'smoreutils
进行解析(是一个使用但与 ' 相反的perl 脚本,它不是 's 的核心模块之一,因此可能不会安装在您的系统上):perl
Time::Piece
ts
Date::Parse
Time::Piece
perl
CUT=2019-12-04T02:50:00-0800 perl -MTime::Piece -F'[][]' -ale '
BEGIN{$cut = Time::Piece->strptime($ENV{CUT}, "%FT%T%z")}
print if /failed login/i &&
Time::Piece->strptime($F[1], "%d/%b/%Y %T %z") >= $cut' < file.log
1 如果我们忽略进行 DST 的时区中冬季/夏季时钟更改时间的光点
答案2
我会留下这个以防万一有人发现它有用,但只需使用这个答案反而。它更简单、更高效。
这是一个 perl 方法:
$ perl -lne 'if(/^\[([^]]+)/){$d=$1; chomp($dateThreshold=`date -d "04 Dec 2019" +%s`); $d=~s|/| |g; chomp($d=`date -d "$d" +%s`); print if $d >= $dateThreshold;} ' test.log
[04/Dec/2019 02:05:13 -0800] access WARNING 10.126.49.92 -anon- - "POST /hue/accounts/login HTTP/1.1"-- Failed login for user: testuser
[04/Dec/2019 02:05:15 -0800] access WARNING 10.126.49.92 -anon- - "POST /hue/accounts/login HTTP/1.1"-- Failed login for user: testuser
[04/Dec/2019 02:04:59 -0800] access INFO 10.126.49.92 ahmed.rao - "POST /notebook/api/check_status HTTP/1.1" returned in 759ms
[04/Dec/2019 02:05:00 -0800] base INFO Selected cluster 0e83a448-26c9-459b-a0f2-3478ecb119af {u'interface': u'impala', u'namespace': u'0e83a448-26c9-459b-a0f2-3478ecb119af', u'type': u'direct', u'id': u'0e83a448-26c9-459b-a0f2-3478ecb119af', u'name': u'0e83a448-26c9-459b-a0f2-3478ecb119af'} interface hiveserver2
[04/Dec/2019 03:05:00 -0800] access INFO 10.126.49.92 ahmed.rao - "POST /notebook/api/close_statement HTTP/1.1" returned in 1345ms
[04/Dec/2019 03:05:00 -0800] base INFO Selected cluster 0e83a448-26c9-459b-a0f2-3478ecb119af {u'interface': u'impala', u'namespace': u'0e83a448-26c9-459b-a0f2-3478ecb119af', u'type': u'direct', u'id': u'0e83a448-26c9-459b-a0f2-3478ecb119af', u'name': u'0e83a448-26c9-459b-a0f2-3478ecb119af'} interface hiveserver2
[04/Dec/2019 03:05:18 -0800] access WARNING 10.126.49.92 -anon- - "POST /hue/accounts/login HTTP/1.1"-- Failed login for user: testuser
而且,更清楚一点:
perl -lne 'if(/^\[([^]]+)/){ ## skip lines that do not match
## Save the date of the current line as $d
$d=$1;
## Replace all slashes with pipes so the 'date' command
## can read this as a date.
$d=~s|/| |g;
## Now, translate $d into seconds since the epoch
chomp($d=`date -d "$d" +%s`);
## Set the threshold date in seconds since the epoch.
chomp($dateThreshold=`date -d "04 Dec 2019" +%s`);
## Print this line if its date is greater than or equal to the threshold
print if $d >= $dateThreshold;
} ' test.log
最后,您可以通过将设置阈值的步骤移动到一个块中来提高效率BEGIN
,这样它在脚本启动时只运行一次:
perl -lne 'BEGIN{chomp($dateThreshold=`date -d "04 Dec 2019" +%s`); } if(/^\[([^]]+)/){$d=$1; $d=~s|/| |g; chomp($d=`date -d "$d" +%s`); print if $d >= $dateThreshold;} ' test.log
答案3
这是使用 GNU 的另一个答案awk
,它诉诸于调用 GNUdate
命令。
该awk
程序(我们称之为find_after_timestamp.awk
)如下所示:
BEGIN{
gsub("/"," ",start_datetime)
extcmd=sprintf("date -d \"%s\" +\"%%Y %%m %%d %%H %%M %%S\"",start_datetime)
extcmd | getline startstring
close(extcmd)
start_ts=mktime(startstring)
print "Lines will be matched starting with timestamp",start_ts
printf("Will look for: \"%s\"\n",searchpat)
}
{
if (match($0,/^\[([[:print:]]*)\][[:print:]]*$/,line_datetime)==0) next
gsub("/"," ",line_datetime[1])
extcmd=sprintf("date -d \"%s\" +\"%%Y %%m %%d %%H %%M %%S\"",line_datetime[1])
extcmd | getline line_dtstring
close(extcmd)
line_ts=mktime(line_dtstring)
if (line_ts > start_ts && $0 ~ searchpat) print
}
你会称其为
awk -v start_datetime="04/Dec/2019 02:05:21 -0800" -v searchpat="[Ff]ailed login" -f find_after_timestamp.awk test.log
其中变量start_datetime
将是搜索范围的开始,即日期/时间等于或晚于该时间点的所有条目都将被考虑。的值start_datetime
必须与日志文件中的格式相同,但除此之外是任意的,并且不需要是文件中实际存在的值。该变量searchpat
将包含您正在寻找的模式。
解释
该构造围绕着将您的(相当“非标准”)日期/时间规范转换
DD/MONTH/YYYY HH:MM:SS TIMEZONE
为 GNU 可以理解的内容,通过使用 . 将日期部分中的date
替换为空格。/
gsub
然后,它通过在 shell 中
date
执行字符串并将结果读入字符串变量(在设置阶段、文件解析阶段)来调用外部命令,该变量现在已格式化,以便内置函数可以解析 ist。extcmd
startstring
line_dtstring
awk
mktime
该
mktime
命令将人类可读的日期/时间规范转换为纯数字的 UNIX 时间,可以使用算术比较来进行比较。在
BEGIN
阶段中,这样做是为了转换开始日期规范,在主体中,这样做是为了转换与当前行关联的时间戳。没有时间戳的行将被忽略 (if (match(...)==0) next
)。如果当前行的时间戳大于(=晚于)参考开始时间戳,并且
searchpat
在该行上找到 ,则将打印该行。
我知道在awk
程序中求助于外部程序有点令人不悦,但这可以使用几乎任何安装中可用的基本工具来完成这项工作。
答案4
使用 sed:
sed -n "/$(date +'%d\/%b\/%Y')/,/*/p" test.log | grep -i "failed login"
- 打印比赛(日期)后的所有内容。
注意:当前日期必须在日志文件中可用。