awk regex magic(匹配每行中第一次出现的字符)

时间:2015-11-20 00:46:28

标签: regex bash apache awk sed

一直在摸着这个,希望有一个我错过的简单解决方案。

摘要

简化以下代码无法应对解析它的(此处略)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)"

进入这样的表,包含vhost名称(无TCP端口号),命中和累积字节数。每个vhost一行:

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美元的字节数。

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

1 个答案:

答案 0 :(得分: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] }'
相关问题