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

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

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

改訂版スクリプト

それでは,今回作った関数を前回のスクリプトに組み込んでみましょう。

リスト4 バージョンチェックスクリプト

  1  #!/bin/sh
  2  
  3  get_version() {
  4    local old_name=`cat /var/log/packages/$1 | grep "^PACKAGE NAME" | cut -f2 -d':' `  
  5    local old_vers=`echo $old_name | cut -f2 -d'-'`
  6    echo  $old_vers
  7  }
  8  
  9  check_vers() {
 10    local old=$1
 11    local new=$2
 12  
 13    for i in 1 2 3 4 5; do
 14      t1=`echo $old | cut -f$i -d'.'`
 15      t2=`echo $new | cut -f$i -d'.'`
 16      if [ $t2 -gt $t1 ]; then
 17        return $t2
 18      fi
 19    done
 20    return 0
 21  }
 22    
  23  if [ $#  = 0 ]; then 
 24      echo "usage: $0 package(s)" 
 25      exit
 26  fi
 27  for tmppkg in $* ; do
 28    pkg=`basename $tmppkg`    
 29    base=`echo $pkg | cut -f1 -d'-'`
 30    chk=`ls /var/log/packages | grep "^$base$" `
 31    if [ "$chk.x" != ".x" ]; then
 32      vers=`echo $pkg | cut -f2 -d'-'`
 33      old_vers=`get_version $base`
 34      check_vers $old_vers $vers
 35      ver_test=$?
 36      if [ $ver_test != "0" ]; then
 37        echo "removepkg $base"
 38      else
 39        echo "same or newer vesion($chk-$old_vers) has been installed."
 40        echo "stop installation for $tmppkg"    
 41        continue
 42      fi
 43    fi
 44    echo "installpkg $tmppkg"
 45  done

3行目から21行目が今回作成した関数です。シェルスクリプトの関数は実行される前に定義しておかないといけないので,スクリプトの先頭部分に置いています。

33行目と34,35行目が今回作成した関数を呼び出している部分です。単純に実行するだけならば関数名に引数を付けて get_version $base などととすればいいのですが,33行目では関数を実行した結果を $old_vers という変数に代入したいので,関数を呼び出す部分を `(バッククォート)で括って,実行結果を $old_vers に代入するようにしています。

一方,34,35行目は関数が return で返す値を受けとる例です。34行目では check_vers という関数に$old_vers, $vers という引数を与えて実行します。関数からreturnで返される値は$?というシェルの特殊変数に収められるので,35行目で$?の値を$ver_testという変数に代入しています。

36行目からは$ver_testの値を調べて,0でなければ新しくインストールしようとしているパッケージの方が新しいので古いバージョンを削除します(38行⁠⁠。一方,$ver_testが0ならば,すでにインストールされているパッケージの方が新しくインストールしようとしているパッケージよりも新しいので,エラーメッセージを出してインストールを中止します(39,40行⁠⁠。

修正とお詫びを1件。この例では30行目にあたるls /var/log/packagesの部分,前回は ls /var/log/packages/* としていましたが,ls /var/log/packages/* とファイル表示にすると,ファイル名がパス名付きで表示されてしまうので,後半の正規表現化したgrepの処理が正しく行われません。

ls /var/log/packages とディレクトリのリスト表示にすると,ファイル名のみが表示されるので,後半のgrep処理も正しく機能します。この部分,テスト時には動いていたのですが,どうも推敲の際に余計な * を付けてしまったようで,お詫びして訂正しておきます。

オプション解析機能

いくつかのパッケージで動作テストしてみると,たいていは上記のコードでうまく行くのですが,openssl-0.9.8i等,バージョン番号に数字以外のアルファベット等が付いている場合,バージョンチェックが正しく動作しないようです。

原因は-gtに代表されるシェルスクリプトの比較演算子が整数の大小を見分ける機能しか持たないことで,アルファベット等の文字列を比較しようとするとエラーになってしまいます。また,比較部分を文字列の比較ができるsortを使っても書き直したとしても,1.2rc1と1.2のどちらが新しいバージョンかを見分けることはできないでしょう。

rc(release candidate)は正式なリリース以前に公開するバージョンに付ける番号ですが,sortではアルファベットは数字よりも後になるので,rcが付いたバージョンの方が大きい(=新しい)と見なされてしまいそうです。

バージョン番号に数字以外が含まれた場合の処理を考えるのは面白いテーマになりそうですが,条件が多様すぎて簡単には扱えそうにありません。そこで今回はバージョンチェックを無視して強制的にインストールするオプションを追加する機能を入れておきましょう。

リスト5 オプション引数の処理

  1  for opt in  $*
  2  do
  3    case $opt in
  4    -f)
  5      force_flag=1 ; shift ;;
  6    -h)
  7      usage ;;
  8    esac
  9  done

この処理は,オプションとして-fと-hを受け付け,-fならば強制インストール用のフラグ($force_flag)を立て,-hならばusageという関数を実行する,というものです。

1行目の $* は引数全てを意味し,すべての引数を1つずつ$optに格納して,$optが-fならば 5行目の処理を,-hならば7行目の処理を行います。5行目でフラグを立てた後のshiftは引数の並びを一つ左にシフトして使用済みの引数(-f)を除くための処理です。

オプション引数を解析するにはgetoptと言う便利な機能もあるのですが,updatepkgコマンドはインストール専用に用意している busybox ベースの環境でも動かしたいので,getopt機能は使わないようにしました。

著者プロフィール

こじまみつひろ

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

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