はじめに
UNIX コマンドが対応している正規表現には、基本正規表現 (BRE) と拡張正規表現 (ERE) の二種類があります。誰もがよく知っている正規表現は拡張正規表現(の拡張版)です。基本的には基本正規表現はもう忘れていいです。ならぜなら、一番の懸念点であった sed
コマンドの拡張正規表現対応 (sed -E
) が POSIX Issue 8 で標準化されることになったからです。
シェルスクリプトでよく使う awk、grep、sed が拡張正規表現に対応したことで、基本正規表現はほぼ不要になりました。拡張正規表現に対応していないのは、あまり必要でない expr や、シェルスクリプトでほとんど使われない ed、ex、more ぐらいです。
とはいえ expr で基本正規表現は使うと言えば使います。で、ふと思ったのですが、基本正規表現と拡張正規表現はエスケープする文字が異なるだけで機能はほとんど同じです(下記参考リンク参照)。それなら拡張正規表現を基本正規表現に変換してやれば、基本正規表現を使う必要はないですよね?ということで実装してみました。これでもう基本正規表現のことは忘れてしまうことが出来ます。
参考リンク
実装(サンプル)
注意 思いつきでざっくり実装しただけのでテストは不十分です。バグがあるかもしれません。
#!/bin/sh
set -eu
# 拡張正規表現(ERE)から基本正規表現(BRE)への変換関数
ere2bre() {
set -- "$1" "$2" ""
while [ "$2" ]; do
case $2 in
"\\+"* | "\\?"* | "\\|"* | "\\{"* | "\\}"* | "\\("* | "\\)"*)
set -- "$1" "${2#?}" "$3"
set -- "$1" "${2#?}" "$3${2%"${2#?}"}"
;;
"+"* | "?"* | "|"* | "{"* | "}"* | "("* | ")"*)
set -- "$1" "${2#?}" "$3\\${2%"${2#?}"}"
;;
*) set -- "$1" "${2#?}" "$3${2%"${2#?}"}" ;;
esac
done
eval "$1=\$3"
}
# 拡張正規表現対応させるための expr のラッパー
expr() {
if [ "${2:-}" = ":" ]; then
ere2bre bre "$3"
set -- "$1" "$2" "$bre"
fi
command expr "$@"
}
# 拡張正規表現で呼び出せる
expr "$(id)" : "uid=([0-9]*)\("
echo "-----"
example() {
ere="$2"; ere2bre bre "$ere"
echo "=== $ere : $bre ==="
printf '%s\n' "$1" | grep --color -E "$ere"
printf '%s\n' "$1" | grep --color "$bre"
}
example 'a\b' 'a\\b'
example 'a(b' 'a\(b'
example 'ab' 'a(b)'
example 'a{b}' 'a\{b\}'
example 'ab' 'a.*'
example 'ac' 'ab?c'
example 'abc' 'ab?c'
example 'a' '(a|b)'
example 'aca' '(a|b)c\1'
example 'az' 'a\z'