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好きですよ。