初歩のシェルスクリプトで遊ぶ[書籍のISBNコードの計算]

ISBN - Wikipedia

シェル関数くみました。
ISBNコードの数字を与えて、チェックディジットを計算、正しければ終了コードが0、間違っていれば終了コードが1です。

  • 普通のシェル変数は使わない。位置パラメータだけで済ませる
  • サブシェルの「()」やコマンド置換は使用禁止
  • 外部コマンドも使用禁止
  • シェルはdash
  • evalは使っていいが、なるべく使わないほうが望ましい

数字やアルファベットを一文字ずつバラバラにして、位置パラメータに代入するにはどうすればいいかな、というのが本題です。

12345 
1 12 123 1234 12345 
1 2 3 4 5

↑だいたいこんな手順です。
一回目のwhileでは、文字列の右側から一文字削ってset、を繰り返し、
二回目のwhileでは、文字列のいちばん右側の文字だけ抜き出して再びset。

#!/bin/sh
# ========================================================================================
# ■ ISBNコードのチェックディジットを検査するシェル関数
# 
# ・10桁と13桁のISBNコードのチェックディジットを計算する
# ・チェックディジットが正常ならば、終了コード0で終了する
# ・チェックディジットが間違っているならば、終了コード1で終了する
# ・ISBNコードとして誤っているならば、終了コード2で終了する
# 
# ▼ 実行方法
# 
# isbnCheckDigitFunc 'ISBNcode'
# 
# 引数1にISBNコードを置いて実行する。
# if文でシェル関数を実行するか、終了コードを「${?}」で判定する。
# 
# 
# 
# ========================================================================================




isbnCheckDigitFunc(){

# ISBNコードに使われない文字が入っていればエラー終了
# ハイフンと半角空白は許す
case "${1}" in
( *[!'0123456789X- ']* )
  return 2
;;
esac

# ---- ISBNコードを1文字ずつ分けて位置パラメータにset -----------------------------
while test "${1%?}" != ''
do
  set -- "${1%?}" "${@}"
done

set -- "${@}" ''

while test "${1}" != ''
do

case "${1#${1%?}}" in
( [0123456789X] )
  set -- "${@}" "${1#${1%?}}"
;;
( * )
  true
;;
esac
  shift 1

done

shift 1

#echo "${@}" # 【debug】


# ---- ISBN10とISBN13それぞれチェックディジット計算 --------------------------------

case "${#}" in
# ----------------------------
( '10' ) # ISBN10
# ----------------------------

set -- $(( ${1} * 10 )) $(( ${2} * 9 ))  $(( ${3} * 8 )) \
       $(( ${4} * 7 ))  $(( ${5} * 6 ))  $(( ${6} * 5 )) \
       $(( ${7} * 4 ))  $(( ${8} * 3 ))  $(( ${9} * 2 )) \
       "${10}"
set -- $(( ${1} + ${2} + ${3} + ${4} + ${5} + ${6} + ${7} + ${8} + ${9} )) "${10}"
set -- $(( $(( 11 - $(( ${1} % 11 )) )) % 11 )) "${2}"


case "${1}" in
( '10' )
  if test "${2}" = 'X'
  then return 0
  else return 1
  fi

;;
(  *  )
  if test "${2}" -eq "${1}"
  then return 0
  else return 1
  fi

;;
esac

;;
# ----------------------------
( '13' ) # ISBN13
# ----------------------------

set -- $(( ${1} + ${3} + ${5} + ${7} + ${9} + ${11} )) \
       $(( $(( ${2} + ${4} + ${6} + ${8} + ${10} + ${12} )) * 3 )) \
       "${13}"
set -- $(( $(( 10 - $(( $(( ${1} + ${2} )) % 10 )) )) % 10 )) "${3}"

if test "${1}" -eq "${2}"
then return 0
else return 1
fi

;;
# ----------------------------
( * ) # ISBN文字数間違い
# ----------------------------
  return 2
;;
esac

}



# 使い方の例

while test "${#}" -ge 1
do

if isbnCheckDigitFunc "${1}"
then
#  echo 'チェックディジット正常' 1>&2
true

else
#  echo 'チェックディジット異常' 1>&2
false

fi

shift 1
done