正規表現とは・基本のメタ文字
正規表現は「文字列のパターン」を表す書き方で、検索や置換を一気に行えます。多くの文字はそのままの意味ですが、特別な働きを持つ文字を「メタ文字」と呼びます。基本は任意の1文字を表す . 、直前の繰り返しを表す * 、行頭を表す ^ 、行末を表す $ の4つです。例えば ^a は行頭の a、b$ は行末の b、a.c は a と c の間に任意の1文字が挟まる並びを指します。まずはこの4つに慣れることが出発点です。
正規表現は、探したい条件を「言葉」ではなく「記号の組み合わせ」で言い表す書き方だ。たとえば何千行もあるログから、error または Error を含む行だけを一瞬で拾い出せる。
人間が目で一行ずつ追うと、大文字小文字の違いでうっかり見落とす。だが条件を式にしておけば、機械は取りこぼさない。
「特定の語で始まる行」「末尾が .conf の行」「数字の並び」といった、ひと言では指しにくい条件も、式にすれば何百行・何千行でも一瞬でふるい分けられる。
Linuxのテキスト処理は、この正規表現を grep・sed・awk といったコマンドに渡して回すのが土台だ。まずはパターンを書く感覚を身につけることが、後のすべての応用につながる。
最初は記号が暗号のように見える。だが覚えるべき基本はごく少数だ。
正規表現の中で特別な働きを持つ記号を、メタ文字と呼ぶ。a や 5 のようなふつうの文字は、その文字そのものを表す。
これに対してメタ文字は、文字そのものではなく「任意の1文字」「直前のくり返し」「行頭」「行末」といった構造や位置を表す。逆に言えば、メタ文字でない文字はすべて書いたとおりの文字として素直にマッチする。だから、まずはどの記号がメタ文字なのかを押さえるのが近道だ。
ここで紹介するのは grep や sed が標準で使う基本正規表現(BRE)の書き方だ。後の項目で出てくる拡張正規表現(ERE)とは一部の記号の扱いが違うが、最初の土台としては BRE を体に入れるのが順当だ。最小の道具箱から開けていこう。
🧰 まず覚えるのは、たった4つでいい
出発点は . * ^ $ の4つだ。
. (ドット)は改行以外の任意の1文字にマッチする。a.c と書けば、a と c のあいだに何か1文字が挟まる並びを指し、abc や a5c などに一致する。
* は直前の文字の0回以上のくり返しを表す。ab* は a だけの場合も含めて a, ab, abb, abbb… のいずれにも一致する。
ここで一つ引っかかってほしい点がある。* は「直前の1文字」に対して効く、ということだ。だから ab* は「ab のくり返し」ではなく「a のあとに b が0個以上」という意味になる。
くり返したいまとまりが複数文字のときは、後の項目で学ぶグループ化が引き受ける。
残る ^ と $ には、ほかの2つと決定的に違う性質がある。文字ではなく「位置」を指すのだ。これをアンカーと呼ぶ。
^ は行頭の位置に、$ は行末の位置に一致する。^a は「a で始まる行」、b$ は「b で終わる行」という意味になる。^a.c$ なら「a で始まり、任意の1文字を挟んで c で終わる、ちょうど3文字の行」を表す。
アンカーは何かの文字に当たるのではなく、行の端という見えない場所に当たる。この一点を最初に飲み込んでおくと、後で混乱しない。
たとえば ^root を grep に渡すと、行のどこかに root があるだけの行ではなく、行頭が root の行だけが選ばれる。
同じ要領で、^abc$ と書けば「中身がちょうど abc だけの行」を、^$ と書けば「中身が何もない空行」を指せる。
アンカーを使いこなせると、行のどこに語があるかまで含めて狙えるようになり、検索の精度が大きく上がる。
🛡 記号そのものを探したいときは、どうする
. や * のようなメタ文字を、記号そのものとして探したくなったらどうするか。直前にバックスラッシュ \ を付けて意味を打ち消す。これをエスケープという。
たとえば \. は「任意の1文字」ではなく「文字としてのドット」を表す。末尾が .conf の行を抜き出したいときに grep '\.conf$' と書くのはこのためだ。
もし \ を付けずに grep '.conf$' とすると、ドットが任意の1文字として働き、aconf や xconf のような行まで拾ってしまう。
「特別な意味を消したいときは \ を付ける」と覚えてほしい。逆に、ただの文字である a や 1 に \ を付ける必要はない。
🔒 なぜパターンはクォートで囲むのか
正規表現を扱ううえで、最初に身につけたい習慣がもう一つある。パターンを必ずシングルクォート '…' で囲むことだ。
理由はこうだ。* ? $ といった記号は、コマンドに届く前にシェルがファイル名展開(グロブ)や変数として先に解釈してしまうことがあり、意図しない結果やエラーを招く。'…' で囲めば、これらの記号はそのまま grep などに渡される。
たとえば ls /etc | grep '\.conf$' は、/etc の一覧のうち末尾が .conf の行だけを残し、chrony.conf や resolv.conf のような行が一致する。
クォートを外すと $ がシェル変数として解釈されたり、* がカレントディレクトリのファイル名に展開されたりして、パターンが壊れてしまう。
式を ' で囲むのは、せっかく書いたパターンをシェルの横取りから守るための鉄則だと考えてほしい。
⚠ つまずいたら、まずこれ
初学者がつまずきやすいのは、^ と $ を「文字」と勘違いすることと、メタ文字のエスケープを忘れることだ。
a$ は「a という文字のあとにドル記号」ではなく「a で終わる行」だ。
また、IPアドレスの 192.168.0.1 を探すつもりで grep '192.168.0.1' と書くと、ドットが任意の1文字として働き 192a168b0c1 のような並びにも当たり得る。厳密に探すなら grep '192\.168\.0\.1' とエスケープする。
もう一つの定番の混乱は、* を「何にでも当たる万能記号」と思い込むことだ。* はあくまで直前の文字のくり返しを表すだけで、それ単体では何も指さない。ファイル名で見慣れた * とは意味が違う、と早めに切り分けておこう。
実務では、設定ファイルから ^# でコメント行を、^$ で空行を指して読み飛ばす、特定の語で始まる行や終わる行だけを抜き出す、といった場面でこの4つが絶え間なく登場する。
まずは「. は任意の1文字、* はくり返し、^ と $ は行頭・行末のアンカー、記号そのものは \ で打ち消し、式は ' で囲む」。この最小セットを手に馴染ませることが、正規表現・メタ文字・アンカーを使いこなす第一歩になる。