【问题标题】:Parsing Apache logs with bash使用 bash 解析 Apache 日志
【发布时间】:2021-01-27 20:53:58
【问题描述】:

我想解析一个 Apache 日志文件,例如:

1.1.1.1 - - [12/Dec/2019:18:25:11 +0100] "GET /endpoint1/ HTTP/1.1" 200 4263 "-" "Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0" "-"
1.1.1.1 - - [13/Dec/2019:18:25:11 +0100] "GET /endpoint1/ HTTP/1.1" 200 4263 "-" "Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0" "-"
2.2.2.2 - - [13/Dec/2019:18:27:11 +0100] "GET /endpoint1/ HTTP/1.1" 200 4263 "-" "Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0" "-"
2.2.2.2 - - [13/Jan/2020:17:15:13 +0100] "GET /endpoint2/ HTTP/1.1" 200 4263 "-" "Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0" "-"
3.3.3.3 - - [13/Jan/2020:17:15:13 +0100] "GET /endpoint2/ HTTP/1.1" 200 4263 "-" "Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0" "-"
1.1.1.1 - - [13/Feb/2020:17:15:13 +0100] "GET /endpoint2/ HTTP/1.1" 200 4263 "-" "Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0" "-"
4.4.4.4 - - [13/Feb/2020:17:15:13 +0100] "GET /endpoint2/ HTTP/1.1" 200 4263 "-" "Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0" "-"
4.4.4.4 - - [13/Feb/2020:17:15:13 +0100] "GET /endpoint2/ HTTP/1.1" 200 4263 "-" "Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0" "-"
4.4.4.4 - - [13/Feb/2020:17:15:13 +0100] "GET /endpoint2/ HTTP/1.1" 200 4263 "-" "Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0" "-"

我需要获取每月访问的客户 IP 列表。我有这样的东西

awk '{print $1,$4}' access.log | grep Dec | cut -d" " -f1 | uniq -c

但这是错误的,因为它每天计算访问 ip。

预期的结果应该是这样的(缩进无关紧要):

Dec 2019
1.1.1.1 2
2.2.2.2 1
Jan 2020
2.2.2.2 1
3.3.3.3 1
Feb 2020
4.4.4.4 3
1.1.1.1 1

其中 2 是 2019 年 12 月 1.1.1.1 ip 的总访问量。

你能建议我一种方法吗?

【问题讨论】:

  • 感谢您的编辑,但示例输入和示例预期输出似乎不同步。例如--> 输出中有20192020,但样本输入没有。
  • 当然,这只是输出的例子:)
  • 我明白了,但是对于将来引用此线程的用户可能会有所帮助,干杯,您也可以查看我的答案,我们也可以在其评论部分进行讨论。
  • 谢谢。我明白了你的观点并改变了示例和预期结果
  • 预期输出中的记录数 (10) 与样本数据中的实际数 (9) 不匹配

标签: linux bash apache shell awk


【解决方案1】:

尽管您的示例预期输出看起来与您显示的示例不匹配,但根据您显示的示例输出和描述,您能否尝试以下操作。此外,由于这是一个日志文件,我将使用 awk 的字段分隔符方法,因为日志将具有固定模式。

awk -F':| |-|/+|]' '
{
  ind[$7 OFS $8 OFS $1]++
  value[$7 OFS $8 OFS $1]=$1
}
END{
  for(i in value){
    split(i,arr," ")
    print arr[1],arr[2] ORS value[i],ind[i]
  }
}' Input_file

说明:为上述添加详细说明。

awk -F':| |-|/+|]' '                             ##Starting awk program from here and setting field separators as : space - / ] here.
{
  ind[$7 OFS $8 OFS $1]++                        ##Creating ind array whose index is 7th 8th and 1st field and keep increasing value with 1 here.
  value[$7 OFS $8 OFS $1]=$1                     ##Creating value with index of 7th, 8th and 1st field and its value is 1st field.
}
END{                                             ##Starting END block of this program from here.
  for(i in value){                               ##Traversing through value elements here.
    split(i,arr," ")                             ##Splitting i into array arr with delimiter as space here.
    print arr[1],arr[2] ORS value[i],ind[i]      ##Printing 1st and 2nd element of arr with ORS(new line) and array value and ind value here.
  }
}' Input_file                                    ##Mentioning Input_file name here.

【讨论】:

  • 谢谢,我试过你的例子,但它似乎没有按预期工作:(
  • @Andrii,我已根据您的示例对其进行了编辑,请立即查看 :)
  • 谢谢!我扩展了示例和预期结果,似乎它计数错误:(
  • @Andrii,再次编辑,请现在检查并通知我。
  • 非常感谢!有用!但不知道如何提供按月/年排序的输出,因为我不清楚它是如何工作的:) 如果您有时间并希望您能为您的 awk 命令提供一些小的解释吗?我认为这对很多用户会有帮助!
【解决方案2】:

试试这个..

外壳:

#!/usr/bin/env bash
LOG_FILE=$1

#regex to find mmm/yyyy
dateUniq=`grep -oP '(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\/\d{4}' $LOG_FILE | sort | uniq`


for i in $dateUniq
do  
    #output mmm yyyy
    echo $i | sed 's/\// /g'
    
    #regex to find ip
    ipUniq=`grep $i $LOG_FILE | grep -oP '(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)'  | sort | uniq`
    
    for x in $ipUniq
    do  
        count=`grep $i $LOG_FILE |grep -c $x`
        #output count ip
        echo $count $x
    done
    echo
done

输出:

Dec 2019
2 1.1.1.1
1 2.2.2.2

Feb 2020
1 1.1.1.1
3 4.4.4.4

Jan 2020
1 2.2.2.2
1 3.3.3.3

【讨论】:

  • 谢谢!似乎 -P 选项(perl regexp)在 MacOS 上不起作用:(
  • 我认为这也可以使用 ipUniq=grep $i $LOG_FILE | grep -oP '\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}' |sort | uniq #regex 来查找 ip
【解决方案3】:

一个用于 GNU awk,按数据输入的顺序输出(即,日志记录等时间数据应按该顺序输出):

$ gawk '                     # using GNU awk
BEGIN {
    a[""][""]                # initialize a 2D array
}
{
    split($4,t,/[/:]/)       # split datetime 
    my=t[2] OFS t[3]         # my=month year
    if(!(my in mye)) {       # if current my unseen
        mye[my]=++myi        # update month year exists array with new index
        mya[myi]=my          # chronology is made
    }
    a[mye[my]][$1]++         # update record to a hash
}
END {                        # in the end
    # PROCINFO["sorted_in"]="@val_num_desc"  # this may work for ordering visits
    for(i=1;i<=myi;i++) {    # in fed order 
        print mya[i]         # print month year
        for(j in a[i])       # then related ips in no particular order
            print j,a[i][j]  # output ip and count
    }
}' file

输出:

Dec 2019
1.1.1.1 2
2.2.2.2 1
Jan 2020
2.2.2.2 1
3.3.3.3 1
Feb 2020
1.1.1.1 1
4.4.4.4 3

【讨论】:

  • 谢谢!任何方式也可以通过访问对其进行排序?例如:2020 年 2 月\n 4.4.4.4 3\n 1.1.1.1 1 1
  • 如果您取消注释并编辑到:PROCINFO["sorted_in"]="@val_num_desc"。是的,这似乎奏效了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-03-23
  • 1970-01-01
  • 2011-02-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多