初歩のシェルスクリプトで遊ぶ[書籍のISBNコードの計算]
シェル関数くみました。
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