次のような内容のテキストファイルがあるとする。
(セミコロンで項が区切られている。)
abc-1234; def-1234; abc-5678; def-5678; abc-9012; def-9012;このうち、abcから始まる項目だけgrepで検索したいとする。
× ダメな方法
次のようにしてもうまくいかない。(おそらく最初に思いつく表現)
$ grep "abc.*;" testfile.txt
abc-1234; def-1234; abc-5678; def-5678; abc-9012; def-9012;
しかし、赤で示されているようにすべてが検索されてしまう。
これは、正規表現の「.*」ワイルドカード部分が、途中に何度も出てくる「;」まで含んで検索してしまうためである。
その結果、正規表現中の「;」は、テキストファイル中の一番最後の「;」とようやくマッチすることになる。
これは最長一致と呼ばれる。
そうではなくて、ターゲット文字列にabcが登場した後に最初に出て来る「;」とマッチするようにして、セミコロンで区切られた各項ごとに照合したい。
この場合は最短一致と呼ばれる。
○次のようにすれば最短一致となる。
$ grep "abc[^;]*;" testfile.txt
abc-1234; def-1234; abc-5678; def-5678; abc-9012; def-9012;
「;」でデータ項目が区切られているのだから、「;」はデータ中で区切るという特別な意味を持っている。
この場合は「.*」の代わりに、「 [^;]* 」を用いることがポイントである。
「.*」の「.」は任意の文字を表すので、区切りであろうが関係なくマッチして、最長一致になってしまったのだった。
そうならないように、ここでは「 [^;] 」を用いている。
「 [^;] 」は「;」以外の任意の文字を表す。
そして「 [^;]* 」は、「;」以外の任意の文字の繰り返しを表す。
だから、検索中に項目の区切り「;」がデータに登場すると、それはもはや「 [^;] 」にはマッチしなくなり、
正規表現中の最後の表現「;」に処理が移ることになる。
これによって、正規表現の三要素で構成されるパターン、
すなわち「abc」、「 [^;]* 」、「;」が順番にマッチしていき、
意図したように「abc」から始まってから最短で出て来る「;」で終わるパターンが検索できるようになる。
■同様に、sedでの置換も試してみる。
sedは指定したテキストファイル全体を、
正規表現で検索し、マッチした部分を指定した文字列に置換できる。
それには、次の構文を使う。
sed -e "s/検索用正規表現/文字列/g"
ここでは、検索でマッチした文字列を、「/**/」に置き換えたい。
「/**/」で使われている「/」「*」は制御記号と区別するために「\」でエスケープしなければならない。
では、ダメな方法から試してみる。
×ダメな方法(さきほどと同じように最長マッチしてしまう。)
$ sed -e "s/abc.*;/ \/*\*\//g" testfile.txt
/**/先程と同じようにテキストの全部にマッチしてしまい、全体が「/**/」で置換されてしまう。
○意図した動作をする方法(最短マッチ)
$ sed -e "s/abc[^;]*;/ \/*\*\//g" testfile.txt
/**/ def-1234; /**/ def-5678; /**/ def-9012;「abc」から始まってから最短で出て来る「;」で終わるパターンが検索されて、
個々が「/**/」に置換される。