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

标签 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 端口号)、命中和累积字节数。每个虚拟主机一行:

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 正则表达式中删除该字符。但我不认为 native 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 美元的字节数。

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

最佳答案

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

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

关于regex - awk regex magic(匹配每行中第一次出现的字符),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33817109/

相关文章:

bash - 自定义 bash 完成输出 : each suggestion on a new line

linux - 将域列表添加到 apache 虚拟主机的 shell 脚本

apache - 轴故障 : Namespace mismatch error when called from certain client such as Docuware

java - 为什么 "||".split ("\\|").length 返回 0 而不是 3?

java - "\1"在这个 Java 字符串中代表什么?

r - 如何用 R 在字符串中的最后一个斜杠后用 "_"替换空格

javascript - 小于或大于除 html 标签之外的字符?

bash - 在 awk 中返回两个变量

c++ - 一种为在 bash 中调用的程序提供参数的方法

PHP 脚本在 5 分钟后停止执行