grepと正規表現
101 試験の正規表現は、メタ文字の意味そのものよりも基本正規表現 (BRE) と拡張正規表現 (ERE) のどちらで書かれているかを判別させる形で問われます。同じパターンでも解釈が変わるものがあるからです。
歴史的な事情で、grep は既定で BRE を解釈します。ERE を使いたいときは -E を付けます。かつての egrep は現在 grep -E の別名という扱いで、非推奨の警告が出る実装もあります。fgrep は grep -F の別名で、正規表現として解釈せず固定文字列として検索します。
バックスラッシュが要るかどうかで割れます
| 意味 | BRE での書き方 | ERE での書き方 |
|---|---|---|
| 1回以上の繰り返し | \+ | + |
| 0回か1回 | \? | ? |
| 選択 | | | ` |
| グループ化 | \(...\) | (...) |
| 回数指定 | \{n,m\} | {n,m} |
BRE では + ? | () {} にバックスラッシュが要り、ERE では要りません。BRE で a+ と書くとプラス記号そのものを探すことになり、意図した繰り返しになりません。試験ではこの取り違えを狙った選択肢が必ず混ざります。
一方、^ (行頭)、$ (行末)、. (任意の1文字)、* (直前の0回以上の繰り返し)、[abc] (いずれか1文字)、[^abc] (それ以外の1文字)、[a-z] (範囲) は、BRE でも ERE でも同じです。* だけはバックスラッシュ無しで使える点に注意してください。+ が特別扱いなのは、BRE が作られた当時の記法にプラスが無かったという歴史的な理由です。
角かっこの中では意味が変わります。[^...] の否定はかっこの先頭に置いたときだけ有効で、途中の ^ はただの文字です。[a-z] のハイフンも、先頭か末尾に置けばただの文字になります。
書き比べ
同じ条件を BRE と ERE で書くと、次のようになります。
ターミナル
grep '^tokyo\|^osaka' city.txt
grep -E '^(tokyo|osaka)' city.txt
grep 'colou\?r' notes.txt
grep -E 'colou?r' notes.txt
grep '[0-9]\+kg' weight.txt
grep -E '[0-9]+kg' weight.txtパターンはシングルクォートで囲むのが安全です。$ や * や ? はシェルにも意味がある文字なので、囲まないと意図しない展開が起きます。
grep の主なオプション
| オプション | 意味 |
|---|---|
-i | 大文字と小文字を区別しない |
-v | マッチしない行を出す |
-c | マッチした行数だけを出す |
-n | 行番号を付ける |
-l | マッチしたファイル名だけを出す |
-r | ディレクトリを再帰的にたどる |
-w | 単語単位で一致させる |
-o | マッチした部分だけを出す |
-E | 拡張正規表現として解釈する |
-F | 正規表現でなく固定文字列として検索する |
-A n / -B n | マッチ行の後ろ / 前 n 行も出す |
-c は「マッチした回数」ではなく「マッチした行数」を返します。1行に2箇所あっても 1 と数えます。回数を数えたいときは -o で切り出してから wc -l に渡します。
-w は単語境界で一致を判定するので、grep -w cat は catalog にマッチしません。
よくある引っかけ
egrep は BRE を使う、という選択肢は誤りです。ERE です。
BRE の a+ が a の1回以上の繰り返しにマッチする、という選択肢も誤りです。BRE ではプラス記号そのものを探します。
. が0文字以上にマッチするという選択肢も誤りで、任意の1文字です。0文字以上は .* です。