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