bashの正規表現マッチングの使い方

あるフリーウェアのインストール用bashプログラムを見ていたら次の様なものがあった。(以下の例は簡略化してある。)

STRING="abc"
if [[ "$STRING" =~ "^ab[cd]$" ]]; then
	echo "matched"
else
	echo "unmatched"
fi

if文の条件表現の中に“=~”というオペレータがある。manを見ると =~ オペレータは文字列を正規表現と比較するとある。知らなかった。bashの文字列比較で正規表現が使えるとは。私は正規表現比較が必要な時はgrepに喰わせて判断していたのだが、これを使えば簡単にできる。

ところが、だ。上のプログラムが期待通りに動かない。上の例では =~ の左辺と右辺の正規表現はマッチするはずだが、結果は“unmatched”。色々と正規表現を変えてみたが、どれとしてマッチングしない。しかし、if文を次のように変えると期待通りに動く(当たり前なのだが)。

if [[ "$STRING" =~ "abc" ]]; then

"abc"以外の正規表現ではマッチングしない。bashのバグか? bashのバージョン3.2.39でも4.0.33でも結果は同じ。そんな長い間バグが修正されてない訳もないので、これは使い方が悪いのだろう。(人のプログラムを疑う前に先ず自分を疑おう!)

で、色々とやってみたら、プログラムを次のように変更するべきだと分かった。

STRING="abc"
if [[ "$STRING" =~ ^ab[cd]$ ]]; then
	echo "matched"
else
	echo "unmatched"
fi

つまり正規表現全体を""(ダブルクォート)で囲んではいけないのだ。ダブルクォートで囲んだ "^ab[cd]$" は正規表現ではなく ^ab[cd]$ という文字列として解釈される。“[[”は外部コマンドではなく、単なるbashのオペレータの1つなので、それに渡す引数はダブルクォートで囲む必要はない。逆に、もし“[”のような外部コマンド(“/usr/bin/[”)に渡すのであれば、ダブルクォートで囲む必要があるが。

分かれば単純な話だが、結構ハマった(1時間位?)。

しかし、このフリーウェアのインストール用プログラムはちゃんとテストしてリリースしたのだろうか? 簡単なテストで直ぐに正常に動かないことが分かるはずなのに。う〜ん、このソフトウェアの品質に疑問符がついてしまった。(“フリーウェア”といっても、ちゃんとしたメーカーがリリースしているソフトウェアなのだが。)

まぁ、でも、これでbash正規表現マッチできることがわかったので、これは色々と使えそうだ。