如何使用 awk 比较同一文件中各行的模式

如何使用 awk 比较同一文件中各行的模式

首先,我刚刚开始学习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 循环遍历其输入而不打印每一行(类似于seds-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]是第三个字段(用户名)。

相关内容