ソースコード・リテラシーのススメ

第21回 ソフトウェア・ツールズの活用[完結編]

この記事を読むのに必要な時間:およそ 5 分

前回は実用的なシェルスクリプトの例として,Plamo Linux用のupdatepkgコマンドの雛形を考えてみました。今回はシェルスクリプトの重要な機能である関数(サブルーチン)を使って,このコマンドに前回宿題として残しておいたバージョンチェック機能を追加することにします。

バージョン取得用関数

オープンソースソフトウェア(OSS)の世界では,早めの公開,しばしば公開(release early, release often)を標語にバージョンアップが頻繁に行われ,バージョン番号はそれらの新旧を見分ける重要な指標です。

以前にも紹介したように,バージョン番号の付け方に統一されたルールはなく,ソフトウェアプロジェクトごとに独自のルールで運用しているのが実情ですが,最近のソフトウェアプロジェクトでは “.(ピリオド)を区切りにした3ケタ程度の数字で,各ケタは左からメジャー番号,マイナー番号,リリース(パッチ)番号を示す程度の合意はあります。

前回紹介したようにPlamo Linuxの場合,/var/log/packages/ディレクトリにパッケージのベース名のファイルが作成され,そこにパッケージに関する情報が記録されています。

リスト1 パッケージ情報の例

PACKAGE NAME:     bash-3.2.39-i386-P1
COMPRESSED PACKAGE SIZE:     1213 K
UNCOMPRESSED PACKAGE SIZE:     2190 K
PACKAGE LOCATION: /var/adm/mount/plamo/00_base/bash-3.2.39-i386-P1.tgz  
 ...

バージョン番号はパッケージ名の一部として PACKAGE NAME: 行に記録されているので,インストール済みのパッケージのバージョン番号はこの行から取り出すことができます。バージョン番号を取り出す作業は他の処理とは独立しているので,関数として作成しておけば便利でしょう。

シェルスクリプトでは,関数は関数名の後ろに( )(丸カッコ)を付けて定義し,処理は { } (波カッコ)で囲んだ部分に記述します。この関数をget_version()という名前にしてみましょう。

リスト2 バージョン番号取得スクリプト

  1  get_version() {
  2    local old_name=`cat /var/log/packages/$1 | grep "^PACKAGE NAME" | cut -f2 -d':' `  
  3    local old_vers=`echo $old_name | cut -f2 -d'-'`
  4    echo  $old_vers
  5  }

2行目と3行目にあるlocalという宣言は,指定した変数をこの関数のみで参照できるローカル変数に定義して,外部での変数名の衝突を防いでいます。

2行目にある$1は関数に与えた引数の指定で,呼び出し側からget_version bashのように関数を呼び出すと,$1は1つ目の引数であるbashに置換されcat /var/log/package/bashが実行されます。

パイプの後半ではgrepコマンドでPACKAGE NAMEという行を取り出して,次のcutで “:(コロン)を区切りに2つの部分に分け,パッケージ名を含む2つめの部分を取り出します。この処理の結果を$old_nameという変数に代入しておき,3行目では,この変数からcutで-(ハイフン)を区切りにパッケージ名を分割し,バージョン部分である2つめの部分を $old_vers に代入し,4行目で $old_vers をechoで呼び出し元に返すようにしています。

シェルスクリプトでは,関数を実行した結果の戻り値は return で指定するか,最後に実行したコマンドの結果として返すことができます。ただし,returnで返せる値は数字に限られており,バージョン番号のような文字列を返すことはできませんので,ここではechoを実行した結果として$old_versを返すようにしました。

バージョンチェック用関数

次に,インストール済みのパッケージと新しくインストールしようとしているパッケージのバージョンを比較する処理を作成しましょう。この処理は2つのバージョン番号を受け取って比較する関数にしてみます。

リスト3 バージョンの比較スクリプト

  1  check_vers() {
  2    local old=$1
  3    local new=$2
  4
  5    for i in 1 2 3 4 5; do
  6      t1=`echo $old | cut -f$i -d'.'`  
  7      t2=`echo $new | cut -f$i -d'.'`
  8      if [ $t2 -gt $t1 ]; then
  9        return $t2
 10      fi
 11    done
 12    return 0
 13  }

この関数では5行目から11行目のループの部分で,x.y.zと表記されるバージョン番号に対し,cutコマンドを使って.を区切り文字-d'.'にし,左から順に各欄を取り出して-f$i新旧の比較をしています。

8行目はシェルのtest 機能([ .. ])を用いて,2番目の引数に指定したバージョン名の各欄の値$t2が,最初の引数の各欄の値$t1よりも大きいかどうか-gtを調べています。

x.y.zと表記されるバージョン番号のうち,どこか1つでも$t2の方が$t1よりも大きければ,2番目の引数で指定した$newの方が$oldよりも新しいと判断できるので,ループ処理を打ち切って,その大きかった数字を9行目のreturn $t2で返しています。

一方,バージョン番号の各欄を全て調べても最初の引数($old)の方が大きかった場合は,12行目で0を返すようにしました。このようにしておけば,呼び出し側で戻り値を調べて0以外の数字が帰ってくれば,2番目の引数に与えたバージョンの方が新しく,0が戻れば最初の引数のバージョンの方が新しいと判断できるでしょう。

なお,先にバージョン番号は「3ケタ程度の数字」と述べましたが,Linuxカーネル(2.6.27.10)やFirefox(2.0.0.18)のように4ケタを使っているソフトウェアも存在するため,念のため5ケタ分をチェックするようにしています。

著者プロフィール

こじまみつひろ

Plamo Linuxとりまとめ役。もともとは人類学的にハッカー文化を研究しようとしていたのが,いつの間にかミイラ取りがミイラになってOSSを仕事にするようになってしまいました。最近はスペシャリスト養成を目的とした専門職大学院で教壇に立ったりもしています。

URLhttp://www.linet.gr.jp/~kojima/Plamo/index.html

コメント

コメントの記入