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

dsekswqp  于 12个月前  发布在  其他
关注(0)|答案(2)|浏览(110)

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

摘要

下面的代码无法科普解析到的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"


我想一个解决办法是mangle变量$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的字节计数。
因此,在写这篇文章的时候,我已经给了自己一个解决方案,但我相信有人会知道一个更好更有效的方法。

6qfn3psc

6qfn3psc1#

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

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] }'

km0tfn4u

km0tfn4u2#

我们的想法是不要再担心IPv4IPv6,只需让FS足够灵活地处理它,而无需拆分额外的数组v

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

个字符
要使regex更能适应尾部的额外双引号字符串,请尝试修改RS

gawk 'END { for (_ in __) printf("%s %d\n", _, __[_]) 

        } { __[$!_] += $NF }' FS=':.+ ' RS='([^0-9]*"[^"]*")?\n'

相关问题