vimで検索していたA氏はふと考えた。
vimとemacsの論争。
vimはモードの概念がある。
emacsはOSと呼ばれるほど多機能である。
「vimは含むけどemacsは含まない行が欲しい!」
つまり、2行目だけを取り出したいとする。
まずは解答。
/\v^(.*vim)@=(.*emacs)@!.*$
前置き
- vim使いである
- 基本的な正規表現を知っている
- vimで検索時に
\v
を付け、verymagic
を有効にする- いちいち
(
などをエスケープするのが大変なため
- いちいち
たとえば、
/\v^a[iue]o$
が何をしているかわかれば大丈夫。
aio
auo
aeo
にマッチします。
以降、/
と\v
は省略。
本題
テストに使う文字列は以下。
mac
tac
cam
miv
emacs
vi
vim
elvim
前にvi
があるm
にマッチさせる
vim
elvim
のm
にマッチさせたい場合。
肯定後読みを利用。
(vi)@<=m
これで、手前にvi
がある場合のみm
にマッチする。()
でくくり、次に@<=
を付ける。
よく使うパターンだし、長いので、\zs
という書き方が用意されている(s
はstart
)。
vi\zsm
前にvi
がないm
にマッチさせる
否定後読みを利用。
mac
tac
cam
miv
emacs
の各m
にマッチ。
(vi)@<!m
=
を!
に変えたら否定。
次にm
が続くvi
にマッチさせる
今度はvi
を抽出したい。
vim
elvim
のvi
にマッチ。
こういうときは肯定先読みを利用。
vi(m)@=
後読みのときの<
が消え、マッチング対象の後ろに来た形。
これもよく使うので、
vi\zem
と\ze
で修飾できる(e
はend
)。
次にm
が続かないvi
にマッチさせる
マッチ対象は以下。
vi
肯定先読みの=
を!
に変えて否定の意味にするだけ。
vi(m)@!
ちゃんとvim
やelvim
は結果から除外されている。
v
とm
を含む行にマッチさせる
ここからが本番。
一般的なweb検索のようにand条件でマッチングさせたい場合。
^(.*v)@=(.*m)@=.*$
ちょっと複雑だけど、肯定先読みがポイント。
最初の^
を読み取るとき、その後に「なんちゃらv
」「なんちゃらm
」が含まれているかどうかをチェック。
条件を満たしていたら.*$
の部分にマッチ。
この条件では、
miv
vim
elvim
の行全体にマッチする。miv
はm
とv
が逆だが、問題ない。
v
かm
を含む行にマッチさせる
今度はor検索。
andとは違って基本的な正規表現だけで書ける。
^.*(v|m).*$
文字列中にv
かm
があるかどうかをチェックしているだけ。
mac
cam
miv
emacs
vim
vi
elvim
にマッチする。
なお、v
もm
も1文字なので、文字セットを利用して、
^.*[vm].*$
と書いても同じ。
v
もm
も含まない行にマッチさせる
「v
とm
を含む行にマッチさせる」を反転させたもの。
^(.*v)@!(.*m)@!.*$
=
が!
に替わっているだけ。
なお、検索対象が1文字だけなら、文字セットを利用して、
^[^vm]*$
と書ける。
v
があるがm
がない行にマッチさせる
vi
にマッチする正規表現。
ここまできたら今までの応用で、
^(.*v)@=(.*m)@!.*$
と、v
側は肯定、m
側は否定にすればいい。
また、例のごとくm
が1文字なのを利用して、
^(.*v)@=[^m]*$
とも書ける。
終わりに
「vimの行だけ見たいんや、emacsは目に入れたくないんや!」というときは、
おもむろに、
/\v^(.*vim)@=(.*emacs)@!.*$
と入力しよう。vim
だけが含まれている行がハイライトされる。
言い忘れていたけれど、私はemacs好きですよ。
……なお、これはvimじゃなくても肯定先読み、否定先読みができればOK。
たとえばperl正規表現の-P
オプションが使えるLinuxのgrep
では、
$ grep -P -r '^(?=.*\Walias\W)(?=.*\Wls\W).*$'# \W は単語区切り
とやれば、「ls
に対してalias
を設定している行だけ」を抽出できる。
まるで検索エンジン!
$ grep -r 'alias'| grep 'ls'$ sed '/emacs/d'
とかはシェル芸に踏み込んでしまうのでここまで。