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