LoginSignup
4
3

expr コマンドで数値判定を行う(とバグになるシェルスクリプト)

Last updated at Posted at 2023-10-26

はじめに

シェルスクリプトで数値判定(整数判定)を行う方法として expr コマンドを使う方法が紹介されますが、これがうまく行かない例、つまりバグになる例を紹介します。

expr で数値判定を行う

expr で数値判定を行うコードとしてよく見かけるのがこれです。数値ではない値を渡すとエラーになることを利用しています。

$ x = abc
$ expr "$x" + 1
expr: non-integer argument
$ echo $?
2

expr で数値判定を行うとバグになる例

expr コマンドは計算結果が 0 だと偽 (0) を返し、終了ステータスはエラー扱いです。

すべての実装(仕様)
$ expr "-1" + 1; echo "exit status: $?"
0
exit status: 1

GNU コマンドや Solaris では expr コマンドの最初の引数が、lengthsubstrindexmatch という文字の場合に特殊な処理を行います。なお POSIX では最初の引数これらだった場合の動作は未指定となっており POSIX に準拠した動作です(というか移植性がないから未指定とせざるを得ない)

GNU コマンドの場合(Solaris除く)
$ expr "length" + 1; echo "exit status: $?"
1
exit status: 0

正規表現を使った数値判定も考えられますが、length という文字列だと正規表現比較になりません。match を使って GNU 版コマンドとそれ以外で処理を分岐させればなんとかなるかな?

GNU コマンドの場合(Solaris除く)
$ expr 'length' : '[0-9]*$'
expr: syntax error: unexpected argument ‘[0-9]*$

-- はオプションと引数を区別するための引数です。ちなみに FreeBSD 版の expr コマンドは -e というオプションを持っています。

GNU コマンドの場合
$ expr "--" + 1; echo "exit status: $?"
1
exit status: 0

$ # 補足 「--」 をちゃんと書こう
$ expr -- "--" + 1; echo "exit status: $?"
expr: non-integer argument
exit status: 2

さいごに

expr コマンドを使って簡単に数値判定を行う方法があるかは不明です。expr& (AND) とか | (OR) とかカッコが使えるので長く書けばできそうな気はするんですけどね。正規表現を使う方法もありますが、expr コマンドで使う正規表現は基本正規表現で、?+ は POSIX では使えない事になってるので注意してください。

正直な所、どちらにしろ expr コマンドは外部コマンドで遅いので興味がないです。私はこんな感じの関数を使ってそれで判定しています。

is_number() {
  case ${1:-} in
    '' | *[!0-9]* ) return 1
  esac
  return 0
}

もしくは bash、ksh、zsh などなら [[ ... ]] を使うと簡単ですね。

if [[ "$x" =~ ^-?[0-9]+$ ]]; then
  : 数値
fi

test コマンドが終了ステータス 2 を返すことを利用した判定方法もありますが、ksh(ksh93u+)、mksh、OpenBSD sh では動きません。ksh93u+m の POSIX モードなら動きます。あと何年かしたらこの方法を使えるようになるかもしれませんが、現時点の ksh は ksh93+ 止まりで ksh93u+m へ置き換わってはいません。

【macOS 13 などの場合】
$ ksh -c '[ str -eq 0 ]; echo $?'
1

【Solaris 11の場合(str変数を参照している、mkshやOpenBSD shも同様)】
$ ksh -c '[ str -eq 0 ]; echo $?'
0

【ksh93u+m(POSIXモード)の場合は判定できる】
$ ksh -c 'set -o posix; [ str -eq 0 ]; echo $?'
ksh: [: str: bad number
2

この記事は expr コマンドで数値判定を行ってる例を見かけるけど、それ本当にちゃんと動く?と気になったので書いています。

4
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3