UECジャーナル
開眼☆シェルスクリプト 表とグラフを描く ― HTMLファイルの出力
今回のお題:HTMLで表とグラフを描く
シェルスクリプトはCGIとしても活用できる。ここではその基本として、次のようなスクリプトからはじめて、データファイルから表およびグラフを出力するHTMLを作成する。
#!/bin/sh
cat << EOF > ./out.html
<!DOCTYPE html>
<html>
<head><meta charset="UTF-8" /></head>
<body>
$(date)
</body>
</html>
EOF
HTML文書の出力にはヒアドキュメントを使っている。cat << EOFがヒアドキュメントのはじまりだ。ここからEOFまでが> ./out.htmlの指定にしたがってout.htmlファイルに出力される。ヒアドキュメントの途中に$(date)というコマンド置換の指定があるため、この部分はdate(1)の実行結果に置き換わることになる。
表を出力
ここに表を追加する。表の元となるデータは次のようなフィールド形式のファイルとする。ファイル名はHOMERとする。通算本塁打数をまとめたファイルだ。
順位 選手 本塁打 FROM TO 試合 打数 1 王 貞治 868 1959 1980 2831 9250 2 野村 克也 657 1954 1980 3017 10472 3 門田 博光 567 1970 1992 2571 8868 4 山本 浩二 536 1969 1986 2284 8052
このデータを次のようなHTMLのtable要素に置き換えることを考える。
<table>
<tr>
<td>1行1列</td>
<td>1行2列</td>
</tr>
<tr>
<td>2行1列</td>
<td>2行2列</td>
</tr>
</table>
どのように実装してもよいが、たとえば次のようにawk(1)を使う。
#!/bin/sh
tmp=/tmp/$$
awk '{
print " <tr>";
for (i = 1; i <= NF; i++) {
print " <td>" $i "</td>"
};
print " </tr>"
}' ./HOMER > $tmp-table
tee $tmp-html << EOF
<!DOCTYPE html>
<html>
<head><meta charset="utf-8" /></head>
<body>
<h2 style="font-size:18px">通算本塁打</h2>
<table border="1" cellspacing="0">
$(cat $tmp-table)
</table>
</body>
</html>
EOF
rm -f $tmp-*
このシェルスクリプトを実行すると、次のようなHTMLファイルが出力される。
<!DOCTYPE html>
<html>
<head><meta charset="utf-8" /></head>
<body>
<h2 style="font-size:18px">通算本塁打</h2>
<table border="1" cellspacing="0">
<tr>
<td>順位</td>
<td>選手</td>
<td>本塁打</td>
<td>FROM</td>
<td>TO</td>
<td>試合</td>
<td>打数</td>
</tr>
<tr>
<td>1</td>
<td>王 貞治</td>
<td>868</td>
<td>1959</td>
<td>1980</td>
<td>2831</td>
<td>9250</td>
</tr>
<tr>
<td>2</td>
<td>野村 克也</td>
<td>657</td>
<td>1954</td>
<td>1980</td>
<td>3017</td>
<td>10472</td>
</tr>
<tr>
<td>3</td>
<td>門田 博光</td>
<td>567</td>
<td>1970</td>
<td>1992</td>
<td>2571</td>
<td>8868</td>
</tr>
<tr>
<td>4</td>
<td>山本 浩二</td>
<td>536</td>
<td>1969</td>
<td>1986</td>
<td>2284</td>
<td>8052</td>
</tr>
</table>
</body>
</html>
ブラウザでは次のようにレンダリングされる。
| 順位 | 選手 | 本塁打 | FROM | TO | 試合 | 打数 |
| 1 | 王 貞治 | 868 | 1959 | 1980 | 2831 | 9250 |
| 2 | 野村 克也 | 657 | 1954 | 1980 | 3017 | 10472 |
| 3 | 門田 博光 | 567 | 1970 | 1992 | 2571 | 8868 |
| 4 | 山本 浩二 | 536 | 1969 | 1986 | 2284 | 8052 |
グラフを描く
最近のメジャーなブラウザはすべてSVGのレンダリング機能を備えているので、グラフの記述にはこのフォーマットを利用できる。たとえば次のようなHTMLで棒グラフを描画することができる。
<!DOCTYPE html> <html> <head><meta charset="UTF-8" /></head> <body> <svg style="height: 60px;> <text x="10" y="36" style="font-size:16px">USP</text> <rect x="50" y="20" width="300" height="20" fill="white" stroke="black" /> <text x="340" y="36" style="text-anchor:end">00</text> </svg> </body> </html>
ブラウザでレンダリングすると次のようになる。
ここではOpen usp Tukubaiのmojihame(1)を使って該当部分にデータをはめ込む処理を実施する。mojihame(1)は次のように指定されたテンプレートファイルにしたがってデータを出力するコマンド。
$ cat temp 長者番付(秘) AAA %1位 %2さん 納税額%3円 AAA $ cat data 1 松浦 12 2 濱田 8 3 上田 -5 4 法林 -110 $ mojihame -lAAA temp data 長者番付(秘) 1位 松浦さん 納税額12円 2位 濱田さん 納税額8円 3位 上田さん 納税額-5円 4位 法林さん 納税額-110円 $
たとえば次のようなシェルスクリプトでデータファイルから棒グラフを出力する処理になる。
#!/bin/sh -vx
tmp=/tmp/$$
#1:順位 2:選手 3:本塁打 4:FROM 5:TO 6:試合 7:打数
#ヘッダを削る
tail -n +2 ./HOMER |
awk '{
wid = $3 / 2;
print $2, $3, NR*24, NR * 24 + 16, wid + 95, wid
}' > $tmp-data
#1:選手名 2:本塁打数 3:グラフ左上y座標 4:字左下y座標
#5:本塁打数文字右端位置 6:グラフ幅
#テンプレートを準備
cat << EOF > $tmp-template
<!DOCTYPE html>
<html>
<head><meta charset="UTF-8" /></head>
<body>
<svg style="height: 160px; font-size: 16px;">
<!-- RECORDS -->
<text x="10" y="%4">%1</text>
<rect x="100" y="%3" width="%6" height="20" fill="white" stroke="black" />
<text x="%5" y="%4" style="text-anchor:end">%2</text>
<!-- RECORDS -->
</svg>
</body>
</html>
EOF
#レコードをテンプレートに流し込む
mojihame -lRECORDS $tmp-template $tmp-data > $tmp-html
cp $tmp-html out.html
rm -f $tmp-*
exit 0
このシェルスクリプトを実行すると次のような出力が得られる。
<!DOCTYPE html> <html> <head><meta charset="UTF-8" /></head> <body> <svg style="height: 160px; font-size: 16px;"> <text x="10" y="40">王 貞治</text> <rect x="100" y="24" width="434" height="20" fill="white" stroke="black" /> <text x="529" y="40" style="text-anchor:end">868</text> <text x="10" y="64">野村 克也</text> <rect x="100" y="48" width="328.5" height="20" fill="white" stroke="black" /> <text x="423.5" y="64" style="text-anchor:end">657</text> <text x="10" y="88">門田 博光</text> <rect x="100" y="72" width="283.5" height="20" fill="white" stroke="black" /> <text x="378.5" y="88" style="text-anchor:end">567</text> <text x="10" y="112">山本 浩二</text> <rect x="100" y="96" width="268" height="20" fill="white" stroke="black" /> <text x="363" y="112" style="text-anchor:end">536</text> </svg> </body> </html>
ブラウザでのレンダリングは次のようになる。
このようにシェルスクリプトとコマンドのみでデータからグラフを含むHTMLを生成することができる。これはそのままCGIとして活用できるもので、たとえば本サイトはusp Tukubaiを使いシェルスクリプトベースのCGIが動作している。
「Keep it simple, stupid.※1」という言葉が表現しているように、極力シンプルな方法を採用することで、物事をうまく進めることができる。やりたいことをはっきりさせ、それに向けてフィールド形式のファイルやテンプレートファイルを用意する。あとはそれらを組み合わせることで処理を実現できる。複雑化せず、あくまでもシンプルな形式とやり方を保持することが大切だ。
※1 KISSの原則、Clarence "Kelly" Johnson.
Software Design 2012年4月号 上田隆一著、「テキストデータならお手のもの"開眼☆シェルスクリプト" : 【4】表とグラフを描く ― HTMLファイルの出力」より加筆修正後転載。
※ usp Tukubaiはユニバーサル・シェル・プログラミング研究所の登録商標。