LoginSignup
6
3

More than 5 years have passed since last update.

expr の計算結果が0になる時は注意

Posted at

tl;dr

  • exprの計算結果が0になる場合はexit statusが1になる
  • 代わりに算術式展開 $(( )) を使う

概要

ある日、数千件のIDが書かれたファイルを読み込んでMySQLからデータを取得するお仕事がありました。対象のテーブルはID % 20の結果をテーブル名の末尾につけてたのでこんな感じのシェルを書いてました。

users.sh
#!/bin/sh

set -eu

cat '/tmp/users.txt' | while read user_id
do
    mod=`expr $user_id % 20`
    mysql -uroot -proot -h 127.0.0.1 test <<-EOS
        select * from users_${mod} where id = ${user_id} or 1=1
    EOS
done

あー楽勝だわと思ってボーッとしてたらなぜかデータが足りないと。いやいや、数行しか書いてないしバグるわけ無いじゃん。一応何件かデータピックアップして計算したし。と思って、教えてもらったIDをチェックすると確かにデータがない(つд⊂)ゴシゴシ

set -x オプションつけてみると、あれ?mysql実行してなくね?。

debug.sh
#!/bin/sh

set -eux

#cat '/tmp/users.txt' | while read user_id
#do
    user_id=20
    mod=`expr $user_id % 20`
    mysql -uroot -proot -h 127.0.0.1 test <<-EOS
        select * from users_${mod} where id = ${user_id} or 1=1
    EOS
#done

debug.txt
[tmp ]$ sh users.sh
+ user_id=20
++ expr 20 % 20
+ mod=0

原因

同僚に教えてもらったのですが、expr は計算結果が0のときは exit statusが1になるので、 set -e をつけていたのでエラーも何も出さずに止まってました

The expr utility exits with one of the following values:
0       the expression is neither an empty string nor 0.
1       the expression is an empty string or 0.
2       the expression is invalid.

0 = result falseだからexit statusが1なのかな?

もうちょっと検証してみる

$ ret=`expr 20 % 20`
$ echo $?
1
$ echo $ret
0
$ ret=`expr 20 % 3`
$ echo $?
0
$ echo $ret
2

$ ret=$((20 % 20))
$ echo $?
0
$ echo $ret
0
$ ret=$((20 % 3))
$ echo $?
0
$ echo $ret
2

この辺でも言われてた
http://unix.stackexchange.com/questions/63166/bash-e-exits-when-let-or-expr-evaluates-to-0

6
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
6
3