Shellの[[ ... ]]と[ ... ]の違い

主にbashとかkshの話
404 Blog Not Found:perl - glob,readdir, and regexpを読んで、昔Shellのグロブ展開でハマったことを思い出したので適当に書いておく。

Shellスクリプト等で複合コマンドの[[ ... ]]testコマンドと同等の[ ... ]をきちんと使い分けている人は意外と少ないのでは無いかと思う。[[ ... ]]も[ ... ]の違いはグロブ展開するかしないかの違いと言ってよいだろう。

変数$hogeが文字列"hoge"から始まる文字か評価したい場合を考えてみる。

if [ $hoge = hoge* ];then
  # ....
fi
# or
if [ $hoge = "hoge*" ];then
  #....
fi

などとやって怒られた経験のある人はたくさんいると思う。なぜダメかというと、[ ... ]はtestコマンドと同等だからだ。上記を書き換えると

if test $hoge = hoge*;then
 #...
fi
# or
if test $hoge = "hoge*";then
 #...
fi

となる。
testコマンドは立派なコマンドだ。*,?などのパターンマッチ文字があればパス名展開しようと試みる。また、引用符で囲まれていれば単なる文字列として扱われる。

よって、1番目はカレント・ディレクトリにhogeから始まるファイルがあれば、そのファイル名に展開されてしまう。複数ある場合はtestコマンドが評価する時にはtest $hoge = hogefoo hogebarとなり、構文エラーとなってしまう。
2番目は文字列と扱われるため、$hogeが文字列hoge*であるかどうかの評価となる。

そんな場合のために[[ ... ]]がある。こちらはコマンドというよりかは構文に近いものがあり、単語分割パス名展開を行わず、パターンマッチが可能になっている。

if [[ $hoge = hoge* ]];then
  # ....
fi

とすれば、めでたく$hoge変数がhogeから始まる文字かどうかの評価が可能となる。