シェルスクリプトでは、たとえば次のようにif構文やwhile構文を使用する。
if [ "_YES" = "_${ANS}" ] then 処理 fi
while [ 0 < "${i}" ] do 処理 done
次のように書けそうな気がするが、次のようには書けない。
if ["_YES" = "_${ANS}"] then 処理 fi
while [0 < "${i}"] do 処理 done
たとえばifを上記のような書き方で使うと、次のようなエラーが出力される。
if ["_YES" = "_${ANS}"] > then > true > fi [_YES: not found $
["_YES"がクォート処理されたあとの[_YESがコマンドとして処理されていることがわかる。
シェルにおけるifやwhileは、その次に続く処理をコマンドとして処理する。コマンドの実行結果によって、それに続くコマンドを処理するかどうかを振り分けるという処理である。制御構文に見えているifやwhileは、次のような処理をするものにすぎない。
if コマンド 引数1 引数2 ... then コマンドの終了値が0なら処理 fi
while コマンド 引数1 引数2 ... do コマンドの終了値が0なら処理 done
[は制御構文の括弧のように見えるが、これはコマンドだ。現在では処理速度の関係から組み込みコマンドとして実装されていることが多いが、[は基本的にコマンドである。実態はtest(1)コマンドで、多くのUNIXではtest(1)と[(1)はハードリンクで同じファイルとして用意されている。
ls(1)コマンドに-ilオプションを指定してi-node番号を表示させると、[とtestが同じファイルであることを確認できる。名前だけ違うという仕組みになっている。
$ ls -il /bin/[ 683048 -r-xr-xr-x 2 root wheel 11456 4月 9 20:10 /bin/[ $ ls -il /bin/test 683048 -r-xr-xr-x 2 root wheel 11456 4月 9 20:10 /bin/test $
test(1)コマンドは指定された引数を評価して、0またはそれ以外の数値で終了するというコマンド。test(1)コマンドは自分の名前が[であった場合には最後の引数に]を要求するという仕様になっている。このため、一見するとのように制御構文中の比較式のような書き方ができるようになっている。要するにコマンドによるシンタックスギミックだ。
[ "_YES" = "_${ANS}" ]
上記であればコマンドが[で、"_YES"が引数1、=が引数2、"_${ANS}"が引数3、]が引数4となる。[の後や]の前に空白をいれなければならないのは、それは制御構文ではなくコマンドであり、そしてコマンドの引数として処理されているためだ。
シェルスクリプトでは、一見制御構文に見える記述が実はコマンドによるものだっりするので、空白が入っているかどうかが意味が変わってくる。こうした例は他にもあるので、問題が発生した場合にはその記述方法がコマンドの指定方法に合っているかどうかを確認してみるとよい。