我有一个服务器日志文件,其中包含以下格式的许多日志条目:
193.1.172.46 - - [23/Mar/2008:03:57:38 +0000] "GET /robots.txt HTTP/1.0" 404 289 "-" "gsa-crawler (Enterprise; M2-N7RQ5RABCA2JT; [email protected],[email protected])"
我被要求识别所有使用 Google 搜索引擎的条目,然后从中获取查询字符串,并且仅在输出中显示查询字符串。
因此,我使用 grep 命令来识别访问搜索引擎的所有条目,如下所示:
grep "http://www.google.com/search?" logs.txt
这给了我一个这样的条目列表:
143.183.121.3 - - [23/Mar/2008:00:16:59 +0000] "GET /staff/jcarthy/home/2ndYearUnix/usefulcommands2col.pdf HTTP/1.0" 200 78866 "http://www.google.com/search?hl=en&q=frequently+used+unix+aliases&btnG=Google+Search"; "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; InfoPath.1)"
现在,如何仅显示仅显示条目的 hl=en&q=frequently+used+unix+aliases&btnG=Google+Search 部分的列表?
答案1
这里的所有其他解决方案都可能在某些日志条目上失败,例如引用字段内有空格或额外的引号和反斜杠、大写域名、https 而不是 http 或位置字段和引用字段内的关键字。
例如:
1.2.3.4 - - [23/Mar/2008:00:16:59 +0000] "GET /a b/ HTTP/1.0" 200 0 "http://www.google.com/search?..." "Mozilla/4.0"
1.2.3.4 - - [23/Mar/2008:00:16:59 +0000] "GET /i/love/http://www.google.com/search?ing HTTP/1.0" 200 0 "http://www.google.com/search?..." "Mozilla/4.0"
1.2.3.4 - - [23/Mar/2008:00:16:59 +0000] "GET / HTTP/1.0" 200 0 "http://www.google.com/search?spaces in referrer" "Mozilla/4.0"
1.2.3.4 - - [23/Mar/2008:00:16:59 +0000] "GET /nohttpver" 200 0 "http://www.google.com/search?spaces in referrer" "Mozilla/4.0"
1.2.3.4 - - [23/Mar/2008:00:16:59 +0000] "GET /" 200 0 "http://example.org/http://www.google.com/search?spaces in referrer" "Mozilla/4.0"
1.2.3.4 - - [23/Mar/2008:00:16:59 +0000] "GET /" 200 0 "http://WWW.GOOGLE.COM/search?spaces in referrer" "Mozilla/4.0"
为了应对这些,我们首先需要正确提取第二个双引号字段。请注意,Apache 日志文件使用反斜杠来转义多余的引号或其他特殊字符。这意味着简单的正则表达式(例如)还"[^"]*"
不够好。
使用 grep 提取引用字段(第二个双引号字段):
grep -oP '^[^"]+"[^"\\]*(?:\\.[^"\\]*)*"[^"]+"\K[^"\\]*(?:\\.[^"\\]*)*(?=")' logfile.txt
看起来很疯狂!让我们来分解一下:
- 的参数
o
意味着grep
我们只得到该行的匹配部分,而不是它的其余部分 - 的参数
P
告诉grep
它使用 Perl 兼容的正则表达式 - 这里使用的正则表达式的整体结构 ,
...\K...(?=...)
意味着我们正在检查整个模式,但只会输出\K
和之间的内容(?=...)
进一步分解正则表达式:
^[^"]+
– 获取行首和第一行之间的所有内容"
"[^"\\]*(?:\\.[^"\\]*)*"
– 获取整个第一个双引号字符串。看这个答案https://stackoverflow.com/a/5696141/1764245[^"]+
– 获取两个字符串之间的所有内容"\K[^"\\]*(?:\\.[^"\\]*)*(?=")
与上面相同,但是我们有\K
after the first"
开始匹配此后的数据,以及(?=")
在 last 之前停止匹配数据"
。
此后,数据将更容易处理,因为您不再需要担心引号并从日志文件中正确提取字段。
例如,您可以将输出通过管道传输到另一个 grep 中:
grep -oP ... logfile.txt | grep -oPi '^https?://www\.google\.com/search\?\K.*'
这里i
第二个 grep 的选项使其不区分大小写。
或者,您可以将对引荐来源网址开头的检查google.com
直接添加到第一个正则表达式中,并\K
根据需要移动它,但我建议不要这样做,因为最好运行两个正则表达式,它们都做一项工作并且做得比将它们合并成一个其职责不明确的地方。
请注意,如果您想从其他 Google 域收集引荐来源网址,您将需要稍微修改您的正则表达式。谷歌拥有很多搜索域。
如果您不介意潜在地捕获一些非 Google 网站,您可以这样做:
... | grep -oPi '^https?://(www\.)?google\.[a-z]{2,3}(\.[a-z]{2})?/search\?\K.*'
否则,您将需要尝试仅匹配 Google 拥有的搜索域,这是一个不断变化的目标:
... | grep -oPi '^https?://(www\.)?google\.(a[cdelmstz]|b[aefgijsty]|cat|c[acdfghilmnvz]|co\.(ao|bw|c[kr]|i[dln]|jp|k[er]|ls|m[az]|nz|t[hz]|u[gkz]|v[ei]|z[amw])|com(\.(a[fgiru]|b[dhnorz]|c[ouy]|do|e[cgt]|fj|g[hit]|hk|jm|k[hw]|l[bcy]|m[mtxy]|n[afgip]|om|p[aeghkry]|qa|s[abglv]|t[jrw]|u[ay]|v[cn]))?|d[ejkmz]|e[es]|f[imr]|g[aefglmpry]|h[nrtu]|i[emoqst]|j[eo]|k[giz]|l[aiktuv]|m[degklnsuvw]|n[eloru]|p[lnst]|r[osuw]|s[cehikmnort]|t[dgklmnot]|us|v[gu]|ws)/search\?\K.*'
另请注意,如果您想包含 Google 的图像搜索和其他搜索子域,则需要将(www\.)?
上述 grep 命令之一更改为((www|images|other|sub|domains)\.)?
.
答案2
通用版本
awk '$11 ~ /?/ { printf "%s\n",substr($11,1+index($11,"?")) ;}'
在哪里
$11 ~ /\?/
搜索 ?在网址中substr($11,1+index($11,"?")
之后搜索部分?- 请注意,参数未被解析。
- 这不会对 URL 进行转义(例如space将显示为
%20
)
以前的版本
awk '$11 ~ /http:\/\/www.google.com\/search?/ { print substr($11,26) ;}'
在哪里
$11
是持有推荐的字段数,您可能需要调整28
的长度为“http://www.google.com/search?”
答案3
这是一个相当可读的 sed 方法
$ cat log.txt | grep "http://www.google.com/search?" | sed s/^.*search?// | sed s/\"\;.*//
IE
删除行的开头:
s/ # replace a match which is:
^ # from the start of the line
.* # any number of any characters
search? # the text "search?"
// # with nothing (remove it)
然后删除行尾
s/ # replace a match which is:
\" # a double quote (escaped with backslash)
\; # a semicolon (escaped with backslash)
.* # any number of characters
// # with nothing (remove it)
只留下参数
答案4
awk -F"[?|;]" '/google.com\/search/{print $2}' log.txt
awk -F? '/google.com\/search/{gsub(";.*","",$2);print $2}' log.txt