二重括弧拡張
随分前に書いたのでちょっと適当な部分もありますが(言い訳)、 以下参照。
簡単に言うと、シェルスクリプトの中でi
という変数に数字が入っていて
それをインクリメントしたい場合
1
|
|
とするだけで良いよ、という話。
expr
を使って
1
|
|
とする方法もありますが、
expr
は遅い
1
ので、拡張機能を使えるならexpr
は使わないほうが良いです。
(())の終了ステータス
この二重括弧ですが、bashのマニュアルによると
((expression))
The expression is evaluated according to the rules described
below under ARITHMETIC EVALUATION. If the value of the expres-
sion is non-zero, the return status is 0; otherwise the return
status is 1. This is exactly equivalent to let "expression".
となっていて、中身の評価値が0でない場合はstatusとして0を返し、 逆に0の場合は1を返す、という特徴を持つCompound Commands(複合コマンド)の一種です。
0がFalseで0以外がTrueというのは他の言語でよくある仕様だと思いますが、 その結果をシェル的に(0が正常終了、それ以外がエラー終了)返している感じです。
従って
$ ((0)); echo $?
1
$ ((1)); echo $?
0
$ ((-1)); echo $?
0
みたいな感じで中身が0の場合だけシェル的にエラー終了な状態になります。
((i++))の終了ステータス
ここで、次の様なシェルスクリプトを考えます。
1 2 3 4 5 6 7 |
|
単に-2
から0
までを順に出力するだけのスクリプト。実行すると
$ ./ipp.sh
-2
-1
0
$
と、何事もなかったかの様に終了しますが、ここで終了ステータスを調べると
$ echo $?
1
とエラー終了になっています。
これは、最後に評価されるのが((i++))
で、一番最後にはこのi
が0
だからです。
i++
によってi
は1
に変わりますが、
後ろ++
がある場合には評価されるときには変化する前のものが評価されます。
つまりこの場合は最後は((0))
になるわけです。
これを
1 2 3 4 5 6 7 |
|
の様に++i
と置き換えてやれば最後の((++i))
の評価はi
変化後の((1))
となってスクリプトの終了ステータスは0になります。
どちらの場合もスクリプト自体は問題なく動きますが、 このあとに終了ステータスをチェックして何か行うようなことがあると 思わない状態になってしまいます。
((++i))
の場合でも条件によっては同じことが起こります。
これを避けるためには
i=$((i+1))
の様に代入する様な形にするか、もしくは
1 2 3 4 5 6 7 8 |
|
の様に必ず最後にexit 0
や関数であればreturn 0
といった様に
ステータスを含めて終了する様にすれば良いかと。