awk 正则表达式魔法(匹配每行中第一次出现的字符)

问题描述 投票:0回答:2

我一直在为这个问题摸不着头脑,希望有一个我错过的简单解决方案。

总结

简化了以下代码无法处理解析到它的(此处缩写)apache 日志中的 IPv6 地址。我是否可以在解析到 AWK 之前 SED 变量,或者可以更改 AWK 正则表达式以仅匹配 $clog 中每行的第一个“:”?

$ clog='djerk.nl:80 200.87.62.227 - - [20/Nov/2015:01:06:25 +0100] "GET /some_url HTTP/1.1" 404 37252
bogus.com:80 200.87.62.227 - - [20/Nov/2015:01:06:27 +0100] "GET /some_url HTTP/1.1" 404 37262
djerk.nl:80 200.87.62.227 - - [20/Nov/2015:01:06:29 +0100] "GET /another_url HTTP/1.1" 200 11142
ipv6.com:80 2a01:3e8:abcd:320::1 - - [20/Nov/2015:01:35:24 +0100] "GET /some_url HTTP/1.1" 200 273'

$ echo "$clog" | awk -F '[: -]+' '{ vHost[$1]+=$13 } END { for (var in vHost) { printf "%s %.0f\n", var, vHost[var] }}'
> bogus.com 37262
> djerk.nl 48394
> ipv6.com 0

可以看到变量 $clog 的最后一行,捕获了 vhost 域,但没有捕获字节数,字节数应该为 273,而不是 0。

原来的长问题

我遇到的问题是“:”字符。除了其他两个字符(空格和破折号)之外,我还需要 AWK 只匹配它正在评估的每一行中第一次出现的“:”。以下将每行分割为三个字符,这可以正常工作,直到日志条目包含 IPv6 地址。

matrix=$( echo "$clog" | awk -F '[: -]+' '{ vHost[$1]++; Bytes[$1]+=$13 } END { for (var in vHost) { printf "%s %.0f %.0f\n", var, vHost[var], Bytes[var] }}' )

上面的代码转换以下日志条目(包含在变量 $clog 中):

djerk.nl:80 200.87.62.227 - - [20/Nov/2015:01:06:25 +0100] "GET /some_url HTTP/1.1" 404 37252 "-" "Safari/11601.1.56 CFNetwork/760.0.5 Darwin/15.0.0 (x86_64)"
bogus.com:80 200.87.62.227 - - [20/Nov/2015:01:06:27 +0100] "GET /some_url HTTP/1.1" 404 37262 "-" "Safari/11601.1.56 CFNetwork/760.0.5 Darwin/15.0.0 (x86_64)"
djerk.nl:80 200.87.62.227 - - [20/Nov/2015:01:06:29 +0100] "GET /wordpress/2014/ssl-intercept-headaches HTTP/1.1" 200 11142 "-" "Mozilla/5.0 (iPhone; CPU iPhone OS 8_1 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Version/8.0 Mobile/12B410 Safari/600.1.4"
djerk.nl:80 200.87.62.227 - - [20/Nov/2015:01:06:30 +0100] "GET /some_other_url HTTP/1.1" 404 37264 "-" "Safari/11601.1.56 CFNetwork/760.0.5 Darwin/15.0.0 (x86_64)"

进入像这样的表,包含虚拟主机名称(无 TCP 端口号)、点击数和累积字节数。每个虚拟主机一行:

djerk.nl 3 85658
bogus.com 1 37262

但是 IPv6 地址由于其表示法而被无意中分割,这会导致 AWK 在评估这些日志条目时产生虚假输出。 IPv6 日志条目示例:

djerk.nl:80 2a01:3e8:abcd:320::1 - - [20/Nov/2015:01:35:24 +0100] "POST /wordpress/wp-cron.php?doing_wp_cron=*** HTTP/1.0" 200 273 "-" "WordPress; http://www.djerk.nl/wordpress"

我猜想解决方法是修改变量 $clog 来替换第一次出现的“:”,并从 AWK 正则表达式中删除该字符。但我不认为本机 bash 替换能够用多行协商变量。

clog=$(sed 's/:/ /' <<< "$clog")
matrix=$( echo "$clog" | awk -F '[ -]+' '{ vHost[$1]++; Bytes[$1]+=$10 } END { for (var in vHost) { printf "%s %.0f %.0f\n", var, vHost[var], Bytes[var] }}' )

这是有效的,因为 $clog 被引用,它保留了换行符并在每一行上单独运行 sed。结果(如图所示)AWK 行需要调整以忽略“:”并获取 10 美元而不是 13 美元的字节数。

事实证明,在写这篇文章时,我已经给了自己一个解决方案。但我相信有人会知道更好更有效的方法。

regex bash apache awk sed
2个回答
4
投票

只是不要用冒号分割整行。从您提取的字段中删除端口号。

split($1, v, /:/); vHost[v[1]]++; ...

我也不明白为什么你会用破折号分开;不管怎样,字段编号都会被重新编号,所以你最终会得到类似的结果

awk '{ split($1, v, /:/); vHost[v[1]]++; Bytes[v[1]]+=$11 }
   END { for (var in vHost)
        printf "%s %.0f %.0f\n", var, vHost[var], Bytes[var] }'

0
投票
mawk 'END { for (_ in __)
                printf("%s %d\n", _, __[_]) } { __[$!_] += $NF }' FS=':.* '

ipv6.com 273
djerk.nl 48394
bogus.com 37262
© www.soinside.com 2019 - 2024. All rights reserved.