「bashだけで動かない記述」が結構ある
他のシェルだと問題無く動くのに、bashに持ってきたら動かなくなるシェルスクリプトがあってこれまで何度か悩まされてきたので、そろそろここらでまとめることにした。
bashのバグなのか仕様なのか定かではないが、どちらにせよ、どの環境でも動く究極のポータビリティーを目指すシェルスクリプト、すなわちPOSIX原理主義を実践するうえではこの知識は避けては通れないだろう。
$(〜)
の中の$$
と'〜'
$ echo $(echo $$'') # PIDが出ない!
#
$
対策
$$
をダブルクォーテーションで囲んだり、$()の中で使わなかったり、直後にシングルクォーテーションを書かなければよい。逆にそれらの条件がすべて重なると起こるようだ。
$ echo $$'' # $()の外なら出る
12345 #
$ echo $(echo "$$''") # ダブルクォーテーションで囲えば出る
12345 #
$ echo $(echo $$) # 直後に'〜'がなくても出る
12345 #
$
備考
ちなみに$?
等の他のシステム変数では起こらない。
$ echo $(echo $?'')
0
$
$(〜)
の中でcase文が使えない(ver.3まで)
$ echo $(case 1 in 1) echo OK;; esac)
-bash: syntax error near unexpected token `;;'
$
これはどうやらcase文におけるパターン指定に使う)
を、$(〜)
の閉じ括弧と誤認してしまうことが原因のようだ。この問題はバージョン4以降のbashでは解消されているようだ。
対策
$(〜)
の代わりにバッククォートを使えばよい。
$ echo `case 1 in 1) echo OK;; esac`
OK
$
ifやcase構文におけるアクションがコメントだけでは済まされない
ifやcase構文で、ある条件が満たされた時に実行させたい動作を記述する場所に、何もコマンドが書かれていないとエラーになる。コメントだけ書いてもダメ。
$ if true; then
> # 何もしない
> fi
-bash: syntax error near unexpected token `fi'
$
対策
最も差し障りの無いヌルコマンド:
を書けばよい。
$ if true; then
> # 何もしない
> :
> fi
$
未定義変数参照が見逃される
起動時に-u
オプションをつけたり起動後にset -u
を実行すると、未定義なシェル変数を参照した時にエラー扱いにしてくれる。……はずなのだが、${filepath%/*}
のようにパラメーター展開により間接的な参照をした時は見逃してしまう。
$ set -u
$ echo $filepath
-bash: filepath: unbound variable
$ echo ${filepath%/*}
$
もちろん${filepath:-/DEFAULTPATH}
などとして、「シェル変数$filepath
が未定義だったら“/DEFAULTPATH”を返す」ということがしたい場合はエラー扱いしなくてよいし、実際、他のシェルでもそうなっているのだが、POSIX文書に載っている-uオプションの原義を汲み取るなら、今例示したような例外を除いて原則的にエラー扱いすべきだ。
ただ、この問題に関しては「bashのみ」ではなく、少なくともzsh、kshでも起こることを確認した。