首先,我刚刚开始学习awk
,我想挑战自己来完成这个任务bash
,所以我并不是在寻找完整的答案,只是到处的一些提示,以及实现的方法,而不是解决方案。
我基本上得到一个像这样的大日志文件,我必须按如下方式对其进行整理:
- 用户登录、用户更改密码、用户在同一秒内注销(所有 3 个操作必须在 1 秒内完成);
- 这些操作(登录、更改密码、注销)相继发生,中间没有其他动作。
所以我的输出需要像这样,只有与上面的测试匹配的用户的配置文件名称。
fxsciaqulmlk
erdsfsdfsdf
fxsciaqulmla
这里我有日志文件的一部分
Mon, 22 Aug 2016 13:15:39 +0200|178.57.66.225|fxsciaqulmlk| - |user logged in| -
Mon, 22 Aug 2016 13:15:39 +0200|178.57.66.225|fxsciaqulmlk| - |user changed password| -
Mon, 22 Aug 2016 13:15:39 +0200|178.57.66.225|fxsciaqulmlk| - |user logged off| -
Mon, 22 Aug 2016 13:15:42 +0200|178.57.66.225|faaaaaa11111| - |user logged in| -
Mon, 22 Aug 2016 13:15:40 +0200|178.57.66.215|terdsfsdfsdf| - |user logged in| -
Mon, 22 Aug 2016 13:15:49 +0200|178.57.66.215|terdsfsdfsdf| - |user changed password| -
Mon, 22 Aug 2016 13:15:49 +0200|178.57.66.215|terdsfsdfsdf| - |user logged off| -
Mon, 22 Aug 2016 13:15:59 +0200|178.57.66.205|erdsfsdfsdf| - |user logged in| -
Mon, 22 Aug 2016 13:15:59 +0200|178.57.66.205|erdsfsdfsdf| - |user logged in| -
Mon, 22 Aug 2016 13:15:59 +0200|178.57.66.205|erdsfsdfsdf| - |user changed password| -
Mon, 22 Aug 2016 13:15:59 +0200|178.57.66.205|erdsfsdfsdf| - |user logged off| -
Mon, 22 Aug 2016 13:17:50 +0200|178.57.66.205|abcbbabab| - |user logged in| -
Mon, 22 Aug 2016 13:17:50 +0200|178.57.66.205|abcbbabab| - |user changed password| -
Mon, 22 Aug 2016 13:17:50 +0200|178.57.66.205|abcbbabab| - |user changed profile| -
Mon, 22 Aug 2016 13:17:50 +0200|178.57.66.205|abcbbabab| - |user logged off| -
Mon, 22 Aug 2016 13:19:19 +0200|178.56.66.225|fxsciaqulmla| - |user logged in| -
Mon, 22 Aug 2016 13:19:19 +0200|178.56.66.225|fxsciaqulmla| - |user changed password| -
Mon, 22 Aug 2016 13:19:19 +0200|178.56.66.225|fxsciaqulmla| - |user logged off| -
Mon, 22 Aug 2016 13:20:42 +0200|178.57.67.225|faaaa0a11111| - |user logged in| -
这就是我被困住的地方。
#!/bin/bash
LIMIT="3"
LOG_FILE="${1}"
if [[ ! -e "${LOG_FILE}" ]]; then
echo "Cannot open log file: ${LOG_FILE}" >&2
exit 1
else
grep 'changed password' -B1 -A1 ${LOG_FILE} \
| awk '{print $5"\t"$6"\t"$9" "$10}' \
| awk 'BEGIN{FS="|"; OFS="\t"} {print $1,$3,$4}' \
| cut -d " " -f1,3,4,5
....
fi
我的逻辑如下:我想检查“更改的密码”字符串是否在“登录”之后和“注销”之前的行上 - 如果这些匹配,那么我想比较这些动作是在同一秒内完成的。
请让我知道我的逻辑是否良好以及awk
我需要使用什么来完成这项工作。我想一路学习,所以如果你能解释一些东西,我将非常感激。
答案1
您可以在文件上维护一个 3 行窗口,每当找到最后一个模式时,就针对当前和最后 2 行进行测试。
BEGIN {
FS = "|"
text[2] = "user logged in"
text[1] = "user changed password"
text[0] = "user logged off"
}
$5 == text[0] && action[1] == text[1] && action[2] == text[2] &&
$3 == user[1] && $1 == time[1] && $3 == user[2] && $1 == time[2] {
print $3
}
{
time[2] = time[1]; user[2] = user[1]; action[2] = action[1]
time[1] = $1; user[1] = $3; action[1] = $5
}
用法:
$ awk -f tst.awk file
fxsciaqulmlk
erdsfsdfsdf
fxsciaqulmla
该
BEGIN
组在解析第一行之前运行,FS
是字段分隔符。第二组有条件匹配打印用户。它就像意大利面条但有效。顺序很重要,因为
awk
在发现第一个错误时将停止评估每个部分。因此,我们首先测试该行是否涉及“注销”,然后测试前两行是否涉及其他操作。最后一组用于保留 3 行窗口。带 hash 的变量
[1]
指最后一行,带hash 的变量指[2]
倒数第二行。注意:任何未初始化的变量都被视为空字符串(或数字零,类型是松散的)。另外,
awk
数组是关联数组,1
这里2
是哈希值。
对于您希望对前几行进行不同/更多测试的情况,骨架如下所示:
condition_for_last_row {
for (i=1;i<=2;i++) {
n = split(prev[i],arr)
# do comparisons here, arr[1] to arr[n] are
# the fields of the i-th previous row
}
}
{
prev[2] = prev[1]
prev[1] = $0
}
答案2
使用perl代替awk,主要是为了方便Date::Parse模块的str2time()
功能:
$ perl -MDate::Parse -F'\|' -lane '
if (/user logged in/) { $event{$F[2]}{login} = str2time($F[0]) }
elsif (/user changed password/) { $event{$F[2]}{passwd} = str2time($F[0]) }
elsif (/user logged off/ &&
defined($event{$F[2]}{login}) &&
defined($event{$F[2]}{passwd}) &&
(str2time($F[0]) - $event{$F[2]}{login} <= 1)) { print $F[2]; delete $event{$F[2]} }
else { delete $event{$F[2]} }' input.log
fxsciaqulmlk
erdsfsdfsdf
fxsciaqulmla
这个 Perl 单行代码使用日期::解析模块从时间日期集合将日期字段转换为 time_t 值(自纪元以来的秒数,1970 年 1 月 1 日午夜)。
该-n
选项告诉 perl 循环遍历其输入而不打印每一行(类似于sed
s-n
选项),并且每个输入行自动拆分为数组@F
(通过-a
和-F'\|'
选项,这导致 perl 的行为类似于 awk 自动拆分每个行)输入行到 $1、$2、$3 等)。该-l
选项告诉 perl 自动处理输入和输出的行尾换行符。
该脚本会跟踪登录和更改密码的用户,以及这些事件在名为 的哈希(关联数组)中发生的时间%events
。 %events 实际上是一个 Hash-of-Hashes(HoH,详细信息请参阅 Perl Data Structures Cookbook。请参阅 参考资料man perldsc
),其中每个元素(以用户名作为键)是另一个散列(以 或login
作为passwd
键,时间戳为价值)。
如果它看到特定用户的两个事件,然后看到同一用户在登录后 1 秒内注销,它将打印用户名并丢弃该用户名的事件。
如果它看到与该用户相关的任何其他事件,它将丢弃该用户的任何当前事件记录。
它是作为单行脚本编写的,但可以轻松转换为独立脚本。
注意:与 bash 和许多其他语言中的数组一样,perl 数组从 0 开始,而不是 1...所以$F[0]
是第一个字段(日期和时间),$F[2]
是第三个字段(用户名)。