Posted at

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

More than 3 years have passed since last update.


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