基本正規表現 (BRE) と拡張正規表現 (ERE)

POSIX正規表現には、grepコマンドやsedコマンドで使用できる基本正規表現(BRE)と、egrepコマンドやawkコマンドで使用できる拡張正規表現(ERE)があります。一般的にはどういうわけかより多くの特殊文字を持つ正規表現が好まれているようで、BREよりもERE、EREよりもPerl正規表現の方が人気があるように思います。しかし、BREとEREの違いをきっちりと理解されている人はそれほど多くないのではないかと思います。

BREとEREの違い

下記はBREとEREで使用できる正規表現の一覧です。

名前 BRE ERE
Collation-related bracket symbols [==] [::] [..] [==] [::] [..]
Escaped characters \ \
Bracket expression [] []
Grouping \(\) \n () \n
Single-character duplication * \{m,n\} * + ? {m,n}
Concatenation
Anchoring ^ $ ^ $
Alternation (not supported)

見ていただければわかるように、EREで使えてBREで使えないのは、「+」、「?」、「|」の3つです。「+」は「{1,}」、「?」は「{0,1}」に置き換えることができるので、実際にEREで使えてBREで使えないのは、「|」のみです。

しかも、GNU grepGNU sedでは、BREであっても「\|」を用いることができるので、ほとんど差はありません。

次に通常の文字として認識させたい場合にエスケープが必要となる特殊文字を示します。

BRE . [ \ * ^ $
ERE . [ \ ( ) * + ? { | ^ $

BREよりもEREの方がかなり少ないですね。これがBREがEREよりも表現力に乏しいと思われている理由ではないでしょうか。

BREとEREの使い分け

ここまで読んでいただいて、BREもEREも機能面で大差はないことを理解していただけたかと思います。それでは、BREとEREをどのように使い分けるのがよいでしょうか。

コマンドラインで使用する場合

コマンドラインで使用する場合は、どちらかと言うとEREをおすすめします。以下にその理由を説明します。

例として、「abcまたはdef」をBREとEREで書くと、それぞれ下記のようになります。

BRE: abc\|def
ERE: abc|def

続いての例として、abcの3個から5個をBREとEREで書くと、それぞれ下記のようになります。

BRE: abc\{3,5\}
ERE: abc{3,5}

どちらの例においても、BREよりもEREの方がよりも短くなっています。そして、読みやすいと思います。それは、間違えにくいことでもあります。なので、コマンドラインで使用する場合は、どちらかと言うとBREよりもEREをおすすめします。

スクリプト内で使用する場合

スクリプト内で使用する場合は、BREをおすすめします。パターン内にシェル変数を使う場合、一般的には特殊文字エスケープする必要がありますが、BREの場合はエスケープが必要なのは「.[\*^$」の6文字ですが、EREの場合は「.[\()*+?{|^$」の12文字もあります。エスケープ用のシェル関数を定義すればよいだけのことですが、文字数が少ない方がシンプルかつ高速です。

下記の例は、指定されたファイル「$FILE」 のパスが「$DIR/」 で始まる場合は、「$DIR/」 を取り除いて「$FILE」に書き出しています。sed_pattern_string()を通さなかったら、DIR 特殊文字や「/」 が入ってきた場合にうまく動作しません。

sed_pattern_string()
{
  echo "${1-}" | sed -e " \
    "'s/\\/\\\\/g; s/\./\\\./g; s/\*/\\\*/g; s/\^/\\\^/g;'" \
    "'s/\$/\\\$/g; s/\[/\\\[/g; s/\]/\\\]/g; s/\//\\\//g'
}

DIR=$1
shift

DIR_SED=`sed_pattern_string "$DIR/"`

for FILE
do
  test -f "$SRCFILE" || continue
  echo "$FILE" | sed -e "s/^$DIR_SED//" >>"$FILE"
done