bash で数値計算しようとして罠に
bash で数値計算する方法
Bash で数値計算しようとすると
NUM=1
RESULT=$(expr $NUM + 1)
echo $RESULT
これで計算できます。Bashの場合はもっと良い方法として、
NUM=1
RESULT=$(($NUM + 1))
echo $RESULT
この書き方もあります。どうやらこっちの方が早いらしいです。
罠
ある時、 expr
を利用していたものを $(( ... ))
の書き方に変更しました。
すると、把握していなかった expr
と $(( ... ))
の動きの違いにはまり混乱しました。
…
ある日…、01_hoge.tgz
みたいなファイル名があった場合に、02_hoge.tgz
みたいな番号を計算して出そうとしていました。
最初次のように書いていました。
NUM=$(ls *_hoge.tgz | tail -n1 | cut -d_ -f1)
RESULT=$(expr $NUM + 1 )
echo $RESULT
これで動いていました。そして、リファクタリングをして、
NUM=$(ls *_hoge.tgz | tail -n1 | cut -d_ -f1)
RESULT=$(( $NUM + 1 ))
echo $RESULT
このように書き換えました。これも動きました。最初は。
しばらくして、番号が、08_hoge.tgz
が表れた時…
value too great for base (error token is "08")
とエラーが起きました…。
$(( ... ))
は 0xFF(16進数) や 071(8進数)といった表記を解釈するようです。
対して expr
はあくまで10進数の数値であることを前提にしているようです。
今回は、2桁に揃えるために0で埋めていたために、8進数として解釈され、存在しない 8
という数字でエラーが起きました。最初うまく動いていただけに、しばらく気づけませんでした…。
結論、今回は意図して、expr
を使うのが良いということがわかりました。
最後に
$(( ... ))
は0xFF
や010
といった表記を解釈して、expr
はあくまで10進数として解釈するという話は忘れないようにしましょうという話でした。
特に、01, 02, 03, ... みたいな連番を作りたい場合は、特に気を付けましょう。