next up previous contents
Next: ハノイの塔 Up: シェルスクリプトの実例 Previous: 挨拶

配列(表引き)

 

Bシェルには配列操作のコマンドが組み込まれていませんが、シェル変数とコマンドを 上手に操ることにより配列に格納されている値の参照と等価な仕事を実現できます。

listingsitem #!/bin/sh exec 3<&0 <month.name i=0 while read k month do i=`expr $i + 1` eval M_$k='"$month"' done exec 0<&3 3<&- while test $i -gt 0 do eval echo ¥$i -- ¥$M_$i i=`expr $i - 1` done

このスクリプトは大きく分けて配列として扱うシェル変数へ別ファイルから読み込んだ データをセットする部分(5〜9行目)と、それを表示する部分(11〜15行目)から成って います。ここでは12カ月の各月に対応する名称を書いた month.name というファイル用 意して使うことにします。

このスクリプトは最初に read 文を使いファイルからデータを読み込みますが、 この時に5行目から始まる while 文の入力を month.name にリダイレクトして

while read k month
do
    ......
done < month.name
としてはいけません。 理由は「4.5.6 ループのリダイレクト」(p.gif) で 説明したように while文はサブシェルで実行されるためです。 このためループ内のシェル変数はループの外側では参照できません。そこで3行目 の処理となるわけです。「4.7.2 exec」(p.gif) と 「2.3 リダイレクト」(p.gif) とを併せて考えてくださ い。3行目の exec 3<&0 <month.name はまずファイルディスクリプタ0の 入力をファイルディスクリプタ3に変更します。ファイルディスクリプタ0はシェルの標 準入力ですからこの処理で標準入力がファイルディスクリプタ3に保存されることにな ります。続いてファイルディスクリプタ0で month.name を読み込み用にオープンしま すので、それ以降の標準入力はファイル month.name となります。 これでカレントシェルで month.name の内容を read で読むことができるように なりました。

read で読み込んだものを配列状態でシェル変数に格納する部分が8行目です。 month.name から1行目の "3 March" を読み込んだとします。この内容は read 文 によりシェル変数 k に "3"、month に "March" 取り込まれ、8行目に来ます。 eval はシェルが引数を2度評価するのと等価な働きをします。1度目の評価で は $k の置換と右辺の単一引用符が外されますので

M_3="$month"
となります。続く2度目の評価で右辺の $month が置換されます。最終的に 8行目は次のようになり、シェル変数 M_3 への代入操作が実現できます。
M_3="March"
この処理は month.name がEOFになるまで繰り返されます。

while ループから抜けたならば10行目の exec 文でもう一度標準入力を 切り換えます。先ほどファイルディスクリプタ3に保存しておいた元の標準入力を復帰 させ、不要になったファイルディスクリプタ3をクローズします。これで3行目で標準 入力を切り換える前の状態に戻ったことになります。

最後はシェル変数を配列の添字を移動してアクセスする動作を真似て month.name から 読み込んだ内容を表示させます。シェル変数 $iは読み込んだ項目数を保持していますので、 これがゼロになるまで減算しながら表示を行ないます。13行目の eval での シェル変数の変遷はご自身でたどってみてください。



Riichiro Saito
1995年08月29日(火) 11時41分26秒 JST