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>

ブラウザでレンダリングすると次のようになる。

USP 00

ここではOpen usp Tukubaimojihame(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>

ブラウザでのレンダリングは次のようになる。

王 貞治 868 野村 克也 657 門田 博光 567 山本 浩二 536

このようにシェルスクリプトとコマンドのみでデータからグラフを含むHTMLを生成することができる。これはそのままCGIとして活用できるもので、たとえば本サイトはusp Tukubaiを使いシェルスクリプトベースのCGIが動作している。

「Keep it simple, stupid.※1」という言葉が表現しているように、極力シンプルな方法を採用することで、物事をうまく進めることができる。やりたいことをはっきりさせ、それに向けてフィールド形式のファイルやテンプレートファイルを用意する。あとはそれらを組み合わせることで処理を実現できる。複雑化せず、あくまでもシンプルな形式とやり方を保持することが大切だ。

※1 KISSの原則、Clarence "Kelly" Johnson.

Software Design 2012年4月号 上田隆一著、「テキストデータならお手のもの"開眼☆シェルスクリプト" : 【4】表とグラフを描く ― HTMLファイルの出力」より加筆修正後転載。

usp Tukubaiユニバーサル・シェル・プログラミング研究所の登録商標。

last modified: 2014-01-13 16:01:13