ShellScript
awk
FreeBSD

URLエンコード・デコードする

More than 3 years have passed since last update.

Webサーバーログ解析で使いたかった

Webサーバーのログを見ていると、検索ページからジャンプしてきている形跡があった。しかし、検索キーワードはURLエンコードされた状態であり、デコードしないとわからない。果たしてどんなキーワードなのか興味があった。

そこで、FreeBSDのportsに収録されているurlendecというプログラムをサクっとインストールしようとしたら……

32ビット専用だった
# 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ならどこでも動くはずなので公開する。必要な方はコピペして使うといい。

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 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のような各種オプションには対応していないが、変換したいテキストデータを標準入力あるいはファイル(引数で指定)で与えるだけ。簡単でしょ。

ついでにエンコーダーも作った

urlencode
#! /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;
  }
}'

エンコードする必要のない文字(英数字や一部の記号)はそのまま流すようにしてある。

尚、データの与え方はデコーダーと同じ。