ネット通販によくあるアレをシェルスクリプトで作ってみる

# シェルスクリプトの実力を再検証する。

この記事は、Tipsというよりはむしろシェルスクリプトの実力を見直そうよ、という内容の記事だ。コードを詳しく見ればその中にはいくつかTipsが詰まっているかもしれないが……。

それよりも、シェルスクリプトの処理速度や機能を再認識してもらえればと思う。

# ネット通販によくあるアレくらい、/bin/shでだって作れるもん！

シェルスクリプトの実力を示すべく、ネット通販の注文画面なんかでよく見かける **「郵便番号から住所を補完するボタン」** をシェルスクリプトで実装してみた。

## 動作デモとソース

デモは、 **[こちら](http://lab-sakura.richlab.org/ZIP2ADDR/public_html/)** 。ソース一式は、 **[GitHub上](https://github.com/ShellShoccar-jpn/zip2addr)** に置いておいた。

デモ機はIntel Core2Quad Q9450 2.66GHzの1コアだけを使ったVPSで、今どきのライトな部類に入るマシンだと思う。この上で、 **約14万行(レコード)＝地域名12万行+事業所名2万行** ある郵便番号データから目的のデータを検索している（デモページの一番左にあるボタンを押した場合）が……、実感できる速さはいかがなものだろうか？

ちなみに、デモページの右二つのボタンはオマケである。よそのWeb APIをサーバーサイドで叩いて外部サイトに検索してもらい、結果を表示しているだけだ。だが、結果はJSONやXMLテキストで渡ってくるので、 **シェルスクリプトでもJSONやXMLをパースできるよ** という意図を込めて設置している。

## 簡単な構造解説

デモを試したら[ソース](https://github.com/richmikan/zip2addr)も見てもらいたい。クライアントサイドのプログラムは何の変哲もないJavaScriptなので割愛するが、サーバーサイドのプログラムは次の3種類から構成されている。

### 1.郵便番号辞書作成[`mkzipdic_kenall.sh`](https://github.com/richmikan/zip2addr/blob/master/data/mkzipdic_kenall.sh), [`mkzipdic_jigyosyo.sh`](https://github.com/richmikan/zip2addr/blob/master/data/mkzipdic_jigyosyo.sh)

[`data`](https://github.com/richmikan/zip2addr/tree/master/data)ディレクトリーの中にあるシェルスクリプト。これは、[日本郵便のサイト](http://www.post.japanpost.jp/zipcode/download.html)から、Zip圧縮されたCSV形式の郵便番号データをダウンロードしてきて、その中から必要な列（郵便番号、都道府県名、市区町村名、町名）だけを取り出して辞書データファイル（[サンプル(地域名用)](https://github.com/richmikan/zip2addr/blob/master/data/ken_all.txt.sample),[サンプル(事業所用)](https://github.com/richmikan/zip2addr/blob/master/data/jigyosyo.txt.sample)）を作るというもの。

コイツはCGIではなくcronで、毎晩でも毎時間でも実行させるように仕掛けておけばいい。え、「頻繁に実行したら負荷がかかるだろ！」って？大丈夫、HEADメソッドで事前にCSVデータのタイムスタンプを見比べ、新しいものが出てなければ作らないようにしてるから心配なし。

ちなみに、CSVのパースは後述の[自作コマンド](https://gist.github.com/richmikan/3251311#file-parsrc-sh)で行っている。（もちろんシェルスクリプト＋UNIX標準コマンド製）

### 2.住所検索CGI[`zip2addr.ajax.cgi`](https://github.com/richmikan/zip2addr/blob/master/public_html/zip2addr.ajax.cgi)

[`public_html`](https://github.com/richmikan/zip2addr/tree/master/public_html)ディレクトリーの中にあるシェルスクリプト。上記の`mkzipdic.sh`で作成した辞書ファイルからCGI経由で指定された郵便番号に対応する住所をJSON形式にしてクライアントに返す。

ちなみに`zip2addr_jsonapi.ajax.cgi`と`zip2addr_xmlapi.ajax.cgi`は、先に記したとおり、よそのWeb APIに検索を任せるオマケである。

### 3.CSV、JSON、XMLパーサー[`parsr*.sh`](https://gist.github.com/richmikan/3251311)

[`commands`](https://github.com/richmikan/zip2addr/tree/master/commands)ディレクトリーの中にあるシェルスクリプト。役割は題名のとおりだ。[コイツら](https://gist.github.com/richmikan/3251311)を通すと、CSVやJSONやXMLデータの欲しいところが`AWK`や`grep`等で簡単に取り出せるようになって超便利。

先に記したとおり、これらももちろんシェルスクリプト＋UNIX標準コマンドで作られている。

# 実は「速い！」「色々できる！」シェルスクリプト

この記事で結局私が訴えたいのは、この見出しに書いたことである。

## シェルスクリプトなんて遅いから使えないよ

と思っているアナタ。いやいや「ちょっと待ったー！」と言いたい。

シェルスクリプトはグルー言語。シェルスクリプト自身でこなそうとせずに、コマンドを呼んでそれらに任せればいいのだ。コマンドを呼べば、シェルスクリプトはその後コマンドの終了を待っているだけ。UNIX標準コマンドたちはC言語で書かれているため、それらを駆使するシェルスクリプトなら、殆どCプログラムの速さで動くことになる。

また、データの持たせ方も工夫次第。郵便番号はたかだか14万行だから1ファイルでよかったが、もし100万行、1000万行のデータだったら……と思うかもしれない。そうなったら、例えば郵便番号の前3桁ごとにファイル分割して持つまで。頭の3桁の検索は、OSがファイルシステムのハッシュテーブルを使って高速に行ってくれるのでシェルプログラマーが自力で高速検索アルゴリズムを組む必要は、たぶんまだ無い。

## シェルスクリプトは他言語みたいに豊富なライブラリーないし、大したことできないよ

と思っているアナタ。いやいや、たぶんアナタの想像以上にいろんなことできるはず。

例えばこのデモでCSVやJSON、XMLテキストの解析をさせているが確かにそんなことをしてくれるコマンドは無かった。だが、シェルスクリプトを使って自作した。中身（[CSV用](https://gist.github.com/richmikan/3251311#file-parsrc-sh)、[JSON用](https://gist.github.com/richmikan/3251311#file-parsrj-sh)、[XML用](https://gist.github.com/richmikan/3251311#file-parsrx-sh)）を見てもらいたい。シェルスクリプトと`sed`や`AWK`や`grep`、`tr`など、どれも見慣れた構文やコマンドでできている。 **難しいことも、簡単なことの組み合わせで実現できるのがシェルスクリプトの強み** なのだ。（[マイクガンカーズのUNIX哲学定理6,7あたり](http://en.wikipedia.org/wiki/Unix_philosophy#Mike_Gancarz:_The_UNIX_Philosophy)）

「じゃあ、Webアクセスは？」……それもこのデモで示したとおり、`curl`を使えばOK。「メールは？」……それは`/usr/sbin/sendmail`を使えば大丈夫、受信なら[`fetchmail`](http://fetchmail.berlios.de/)コマンドとか。「でも、本格的なRDB操作はさすがに無理でしょ」……いやいや、 **POSIX標準コマンドで[`join`](http://pubs.opengroup.org/onlinepubs/9699919799/utilities/join.html)ってのがあって** これとか`sort`とか`AWK`とか使えば、テキストファイルでも相当本格的な操作が可能。

おかげでネット通販によくあるコレだけじゃなくて、ネット通販サイトそのものを作ったりしちゃったりなんかしちゃったりして