【问题标题】:Linux Bash Shell read log file compare each line to the reset of the fileLinux Bash Shell 读取日志文件将每一行与文件的重置进行比较
【发布时间】:2018-09-13 13:14:00
【问题描述】:

我有这个非常大的身份验证日志文件。我已将它与 SED 和 AWK 命令配对到我需要的信息。现在,我需要在用户连接和从服务器断开连接时配对,以记录用户登录的时间和时间。

在用户登录时的日志中,您可以看到他们的用户名和 IP 端口。当他们注销时,您只能看到 IP 端口。所以我需要匹配那些IP端口,然后从它们连接的线路中吐出信息。

日志文件如下所示:

Date,time,Username,Viewer,IPPort <br>
20180911,12:00,Chris,New,55567 <br>
20180911,12:30,Tom,New,55577                  <<<<<-Connections <br>
20180911,12:45,Larry,New,55587 <br>
20180911,14:00,,,55567 <br>
20180911,15:30,,,55577                 <<<<<-When user logs off <br>
20180911,16:45,,,55587 <br>

我的第一个想法是使用嵌套的 while 循环。因此,转到每一行,然后在该行上再次遍历每一行以查找匹配项。这确实会遍历文件并匹配内容,但它会在嵌套循环中根据自身重新检查顶行。必须有一种更清洁的方法来完成这项工作。

这就是我的循环当前的样子。

INPUT=firstreport.csv
OLDIFS=$IFS
IFS=,
[ ! -f $INPUT ] && { echo "$INPUT file not found"; exit 99; }
while read Date Time Username Viewer IP
do
        echo "IP : $IP"
        IPCHECK=$IP
        while read Date Time Username Viewer IP
    do
    if [[ $IPCHECK == $IP ]]; then
    echo "Match : $IP"
    fi


    done < $INPUT
done < $INPUT
IFS=$OLDIFS`

任何关于我如何完成这项工作的建议将不胜感激。我的最终目标是制作一份可以转储到 Excel 中并显示用户活动地图的报告。

谢谢你, 克里斯

【问题讨论】:

  • 这些端口是否相当独特?如果是这样,按端口排序似乎可以解决您的问题。
  • 是的,所有端口都应该是唯一的,我可以通过某种方式使用 sort 吗?
  • 嗯,我刚刚注意到示例中两种类型的日志的端口不在同一列中(第 5 用于登录,第 4 用于注销)但您的代码似乎没有尝试要处理这个,是不是样本有错误?
  • 假设端口总是在第 5 个逗号分隔的字段中,下面的排序应该给你一个输出,其中具有相同端口的条目是连续的:sort -t, -k 5.1。样品运行here
  • 是的,这是我的示例中的一个错误,它应该保留在整个文件的第 5 列中。惊人的!我会试一试。

标签: linux bash shell loops while-loop


【解决方案1】:

这是 GNU awk:

gawk '
    BEGIN { FS = OFS = "," }
    NR == 1 {next}
    $3 != "" { # connection
        conn[$5]["on"] = $3 FS $4 FS $1 FS $2
    }
    $3 == "" {
        if ($5 in conn) {
            conn[$5]["off"] = $1 FS $2
        }
        else {
            print "Error: found a log off with no log on, line " NR
        }
    }
    END {
        print "IPPort","User","Viewer","ON date","ON time","OFF date","OFF time"
        for (id in conn) {
            print id, conn[id]["on"], conn[id]["off"]
        }
    }
' file
IPPort,User,Viewer,ON date,ON time,OFF date,OFF time
55567,Chris,New,20180911,12:00,20180911,14:00
55577,Tom,New,20180911,12:30,20180911,15:30
55587,Larry,New,20180911,12:45,20180911,16:45

对于较旧的 awks(使用 测试)

awk '
    BEGIN { FS = OFS = "," }
    NR == 1 {next}
    $3 != "" { ids[$5]; conn[$5,"on"] = $3 FS $4 FS $1 FS $2 }
    $3 == "" {
        if ($5 in ids)
            conn[$5,"off"] = $1 FS $2
        else
            print "Error: found a log off with no log on, line " NR
    }
    END {
        print "IPPort","User","Viewer","ON date","ON time","OFF date","OFF time" 
        for (id in ids)
            print id, conn[id,"on"], conn[id,"off"]
    }
' file

【讨论】:

  • 这看起来有点困难,这会代替我的第二个while循环吗?
  • 这将替换外部循环及其中的所有内容。
  • 我一直在使用它,但似乎无法让它运行,它一直在抱怨语法。由于某种原因,它不喜欢 [ ] 括号。
  • 这里是 GNU Awk 3.1.7 的输出
  • 我刚刚在运行 4.0.2 的服务器上尝试过,效果很好!你知道我如何修复语法以在 3.1.7 上工作吗?
【解决方案2】:

鉴于记录的端口相当独特,并且它们始终记录在 cmets 中讨论的第 5 列中,您应该能够使用以下sort 命令按端口对条目进行分组,重新分组所有信息关于用户的连接:

sort -t, -k 5,5

在这个sort 命令中,我们使用-t, 选项指定字段以逗号分隔,然后我们使用-k 5,5 要求sort 仅对第5 个字段进行排序。

(注意:在 cmets 中我建议使用 -k 5.1,这意味着从第 5 个字段的第一个字符开始排序,但是 1).x 字符偏移量默认为开始/结束字段的第一个/最后一个字符位置并且可以省略,并且 2)您可能有比在提取中发布的字段更多的字段,如果未指定结束字段,则将在排序中不必要地使用)

应用于您的示例输入,固定以使端口成为注销条目中的第 5 个字段:

20180911,12:00,Chris,New,55567
20180911,12:30,Tom,New,55577
20180911,12:45,Larry,New,55587
20180911,14:00,,,55567
20180911,15:30,,,55577
20180911,16:45,,,55587

它产生以下输出:

20180911,12:00,Chris,New,55567
20180911,14:00,,,55567
20180911,12:30,Tom,New,55577
20180911,15:30,,,55577
20180911,12:45,Larry,New,55587
20180911,16:45,,,55587

你可以try it here

【讨论】:

    【解决方案3】:

    用这个替换内部循环:

    line=0
    while read Date Time Username Viewer IP COMMENT
    do
      let line=1+$line
      awk -F "$IFS" '
        BEGIN {
          IP="'${IP}'"
          if(!match(IP, "^[0-9]+$")) {exit}
          line='"${line}"'
        }
        NR<line { next }
        NR==line {
          print "CONNECT:",$0
          next
        }
        $5==IP && $4 != "New" {
          print "DISCONNECT:", $0
          exit
        }
        $5==IP {
          print "FOUND RECONNECT BEFORE DISCONNECT"
          exit
        }
      ' $INPUT
    done < $INPUT
    

    稍微改变一下输入,如下所示:

    20180911,12:00,Chris,New,55567,
    20180911,12:30,Tom,New,55577, <<<<<-Connections 
    20180911,12:45,Larry,New,55587, 
    20180911,14:00,,55567, 
    20180911,15:30,,55577, <<<<<-When user logs off 
    20180911,16:45,,55587, 
    20180911,16:45,Tom,New,55577, <<<<<-reconnect
    20180911,16:45,55577, <<<<<-redisconnect
    20180911,16:45,CURLY,New,55577, <<<<<-reconnect
    20180911,16:45,MOE,New,55577, <<<<<- foobar
    20180911,16:45,55577, <<<<<-redisconnect
    

    给出这个:

    CONNECT: 20180911,12:00,Chris,New,55567,
    DISCONNECT: 20180911,14:00,,55567, 
    CONNECT: 20180911,12:30,Tom,New,55577, <<<<<-Connections 
    DISCONNECT: 20180911,15:30,,55577, <<<<<-When user logs off 
    CONNECT: 20180911,12:45,Larry,New,55587, 
    DISCONNECT: 20180911,16:45,,55587, 
    CONNECT: 20180911,16:45,Tom,New,55577, <<<<<-reconnect
    FOUND RECONNECT BEFORE DISCONNECT
    CONNECT: 20180911,16:45,CURLY,New,55577, <<<<<-reconnect
    FOUND RECONNECT BEFORE DISCONNECT
    CONNECT: 20180911,16:45,MOE,New,55577, <<<<<- foobar
    

    我认为这就是您想要的。我怀疑根据您的真实数据,您需要添加更多条件以确保用户和端口有意义。

    巧妙的方法是在 python 或 perl 中执行此操作(整个脚本)并使用前瞻多行正则表达式。

    **** 请注意awk 脚本已更新,但输入/输出仍然是原始的

    【讨论】:

    • 哦。刚刚注意到问题的变化。
    • 我想这正是我想做的,现在修改代码。将打印命令输出到文件的最佳方式是什么?
    • 从命令行:$ this_script.sh &gt; out.txt 或在脚本内部,如“done &lt; $INPUT &gt; $OUTPUT”。
    • 我不确定我在语法上做错了什么。我无法让它输出。
    • 你定义了“$OUTPUT”吗? OUTPUT="/tmp/file.txt"
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-22
    • 2014-10-10
    • 1970-01-01
    相关资源
    最近更新 更多