Webサーバーログ解析で使いたかった
Webサーバーのログを見ていると、検索ページからジャンプしてきている形跡があった。しかし、検索キーワードはURLエンコードされた状態であり、デコードしないとわからない。果たしてどんなキーワードなのか興味があった。
そこで、FreeBSDのportsに収録されているurlendecというプログラムをサクっとインストールしようとしたら……
# cd /usr/ports/net/urlendec
# make install
===> urlendec-1.0 is only for i386, while you are running amd64.
*** [install] Error code 1
Stop in /usr/ports/net/urlendec.
#
これだから、POSIX準拠でないコマンドは信用ならんのじゃ!!!
しょーがないのでデコーダーを自作した
というわけでPOSIX原理主義全開で作った。UNIXならどこでも動くはずなので公開する。必要な方はコピペして使うといい。
#! /bin/sh
case $# in
0) cat - ;;
*) for file in "$@"; do
case "$file" in
-) : ;;
/*) : ;;
*) file="./$file";;
esac
cat "$file"
done ;;
esac |
exec env - awk '
BEGIN {
# --- prepare
LF = sprintf("\n");
OFS = "";
ORS = "";
# --- prepare decoding
for (i=0; i<256; i++) {
l = sprintf("%c",i);
k1 = sprintf("%02x",i);
k2 = substr(k1,1,1) toupper(substr(k1,2,1));
k3 = toupper(substr(k1,1,1)) substr(k1,2,1);
k4 = toupper(k1);
p2c[k1]=l;p2c[k2]=l;p2c[k3]=l;p2c[k4]=l;
}
# --- decode
while (getline line) {
gsub(/\+/, " ", line);
while (length(line)) {
if (match(line,/%[0-9A-Fa-f][0-9A-Fa-f]/)) {
print substr(line,1,RSTART-1), p2c[substr(line,RSTART+1,2)];
line = substr(line,RSTART+RLENGTH);
} else {
print line;
break;
}
}
print LF;
}
}'
GNU版のsedを使えば簡単に書けるのは知っているけど、せっかく作るなら可搬性を高めたいのでPOSIXの範囲で作った。結局AWKにベッタリで、しかも全てをBEGINセクションで済ませるというAWKっぽくない使い方だけど、今回の目的では意味を成さないフィールド分割処理($0,$1,$2,……
を作る)が走らないようにするため。
使い方
オリジナルのurldecodeのような各種オプションには対応していないが、変換したいテキストデータを標準入力あるいはファイル(引数で指定)で与えるだけ。簡単でしょ。
ついでにエンコーダーも作った
#! /bin/sh
case $# in
0) cat - ;;
*) for file in "$@"; do
case "$file" in
-) : ;;
/*) : ;;
*) file="./$file";;
esac
cat "$file"
done ;;
esac |
exec env - awk '
BEGIN {
# --- prepare
LF = sprintf("\n");
OFS = "";
ORS = "";
# --- prepare encoding
for(i= 0;i<256;i++){c2p[sprintf("%c",i)]=sprintf("%%%02X",i);}
c2p[" "]="+";
for(i=48;i< 58;i++){c2p[sprintf("%c",i)]=sprintf("%c",i); }
for(i=65;i< 91;i++){c2p[sprintf("%c",i)]=sprintf("%c",i); }
for(i=97;i<123;i++){c2p[sprintf("%c",i)]=sprintf("%c",i); }
c2p["-"]="-"; c2p["."]="."; c2p["_"]="_"; c2p["~"]="~";
# --- encode
while (getline line) {
for (i=1; i<=length(line); i++) {
print c2p[substr(line,i,1)];
}
print LF;
}
}'
エンコードする必要のない文字(英数字や一部の記号)はそのまま流すようにしてある。
尚、データの与え方はデコーダーと同じ。