Q. awk '{print $9}'とかでUser-Agent丸ごと取り出したい
とか思ったことない? Apacheのログ解析で。でもできないじゃない。例えば
192.168.0.1 - - [17/Apr/2014:11:22:33 +0900] "GET /index.html HTTP/1.1" 200 43206 "https://www.google.co.jp/" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.116 Safari/537.36"
なんかのログで"mozilla/5.0 (Windows NT 6.1 …… Safari/537.36"
の部分が欲しいなーと思って、AWKで取ろうとしても
$ awk '{print $12}' httpd-access.log
"Mozilla/5.0
$
なーんてふうに、最初のスペースまでで切れてしまって全然使い物にならない。さて、何とかならないものか。
A. 6個のsedコマンドにパイプで通せばできるよ
でも、そこは我らがUNIX。シェルスクリプトとパイプと標準コマンドさえあればお手のもの。sedコマンド6個通すだけでできるようになるのさ。
次のシェルスクリプトを書いてこいつに流し込むだけ。
(2014/05/05) おい、動かねーじゃねーか!という指摘を受けて修正しました。m(_ _;)m
#! /bin/sh
# ----- ちょいと下ごしらえ -----
RS=$(printf '\036') # 元々の改行位置にマークするための記号定義
LF=$(printf '\\\n_');LF=${LF%_} # sedで改行コードを挿れるための定義
# ----- 本番 -----
sed 's/^\(.*\)$/\1'"$RS"'/' |
sed 's/"\([^"]*\)"/'"$LF"'"\1"'"$LF"'/g' |
sed 's/\[\([^]]*\)\]/'"$LF"'[\1]'"$LF"'/g' |
sed '/^["[]/s/[[:blank:]]/_/g' | # ここに空白の代替文字
sed 'N;$s/\n//g' |
sed 's/'"$RS"'/'"$LF"'/g'
試しに実行してみると、
$ cat httpd-access.log | apalognorm.sh
192.168.0.1 - - [17/Apr/2014:11:22:33_+0900] "GET_/index.html_HTTP/1.1" 200 43206 "https://www.google.co.jp/" "Mozilla/5.0_(Windows_NT_6.1;_WOW64)_AppleWebKit/537.36_(KHTML,_like_Gecko)_Chrome/34.0.1847.116_Safari/537.36"
$
日時列(4列目)、HTTPリクエストパラメーター列(5列目)、User-Agent列(9列目)に含まれているスペースが全て_
に置換されている。もちろん、列を区切っているスペースはそのままで。
もし、_
が気に食わないのであれば*
でも+
でも、好きな文字列(2文字以上でもいい)を4番目のsedの後半部分に書けばいいだけ。
6つのsedは何をやってるのか?
sed 1号
(加工の都合により、途中で一時的に改行を挿むので)元の改行を別の文字(0x1e)で退避させておく。
sed 2号
ダブルクォーテーションで囲まれている区間"~"
があったら、その前後に改行を挿み、その区間を単独の行にする。
sed 3号
ブラケットで囲まれいる区間[~]
も同様に、前後に改行を挿んで、この区間を単独の行にする。
sed 4号
ダブルクォーテーション、またはブラケットで始まる行は、先程行を独立させた区間なので、これらの行にある空白をそうでない文字列に置換する。
sed 5号
改行を全部取り除く。(これはべつにsedでやる必要ないのだけど)
sed 6号
退避させていた元々の改行を復活させる。(これもべつにsedでやる必要ないのだけど)
コマンド化したものをGitHubに置いておいたよ
例によってちゃんとコマンド化したものをGistに上げておいた。ログ解析で困っているなら使ってみてね。スペースの代替文字が_
では気に入らない人向けに、オプションで指定できるよにしてある本格派だ。
Apacheサーバー管理者は、これで少し幸せになれるかも。
尚、Gistに上げたものは、5号と6号のsedはtrに置き換えているので悪しからず。まぁ、全部sedでやるというのはネタだからねぇ。