(今さら)シェルにおける数値チェック

よくある話だが、やり方をうろ覚えだったので、改めて調べてみた。ちょっと検索してみると、大きく2種類の実装方法があった。

  • 数値演算をやってみて、エラーをひろう方法
  • 値が数値のみで構成されているかを調べる方法


なるほど。どちらの方法でもよいと思う。
後者については、検索結果として、次のようなサイトが(比較的)上位に出てきた。でも、これらはちょっとだけ問題だと思う。

NUMERIC_CHECK=`echo "${1}" | awk '/[^0-9]/ {print}'`
if [[ ! -z $NUMERIC_CHECK ]]
then
    echo "not numeric"
else
    echo "numeric"
fi

BシェルFAQ - torowikiより》

A=`echo -n $1|sed 's/[0-9]//g' `
if [ -n "$A" ]; then
  echo "文字列も含まれます。"
else
  echo "数値だけです。"
fi

modules:bwiki:index.php [fl8 Wiki]より》


やっていることは、「正規表現で数値部分を除去し、残った部分が空であれば、元々の値が数値である」というチェックロジックだ。で、何がまずいかというと、元々の値が空であることが想定されていない点である。例えば、次のようにスクリプトを実行すると、第1引数は数値ではないが、チェックは通ってしまう。

/example/some_script.sh  ""  "abc"  123


なので、この方法を使うのであれば、次のように*1元々の値が空でないこともチェックすべきではないだろうか。

arg1=${1}
expect_empty=`echo -n ${arg1} | sed 's/[0-9]//g'`

if [ "x" = "x${arg1}" ] || [ "x" != "x${expect_empty}" ]
then
    echo "引数1の値 ${arg1} が、数値ではありません。"
fi

ど?

2011/04/21追記

この方法は、数値チェックというよりも、ゼロと自然数チェックだな。小数もダメだし、負の数もダメ。なのに“0000001”や“00”のような文字列っぽい値も数値としてしまう。もう少し考慮が必要だ。

*1:むろん、testコマンドの表現(if文の条件)は“-n”なり“-z”なりを用いてもよい。シェルの人ではない人でも、読みやすくしているだけ。あと“||”でなくtestコマンドの表現である“-o”を使うこともできるが、こっちは可読性ではなく、より効率的かなと思って。