Vimで開いている全バッファを検索する(bufdo + vimgrep + cwindow)
複数ファイルを同時に開いて作業していると、「あの文字列、どのバッファに書いたっけ」と探し回ることがあります。1つずつバッファを切り替えて検索するより、開いている全バッファをまとめて検索し、結果を一覧表示させたほうが速いです。
検証環境:macOS + Vim 9.1.1752。3つのファイル(1つ目に1件・2つ目に0件・3つ目に2件のヒット)を開いた状態で、各コマンドを実際に実行してQuickfixの中身を数えた結果です。
| コマンド | これだけ覚える |
|---|---|
:silent! bufdo vimgrepadd /pattern/g % | 開いている全バッファを検索してQuickfixに貯める |
:cwindow | 結果があるときだけ一覧を開く |
autocmd QuickFixCmdPost *grep* cwindow | grep系コマンド実行のたびに自動でQuickfixを開く |
:vimgrep /pattern/g **/*.js | バッファではなく指定ファイル群を直接検索(別記事) |
全バッファを検索する
:call setqflist([], 'f')
:silent! bufdo vimgrepadd /{検索パターン}/g %
:cwindow
:bufdoで全バッファを巡回し、各バッファに検索を掛けて結果をQuickfixリストに貯め、最後に:cwindowで一覧を開く、という組み合わせです。%はカレントバッファを指すので、巡回しながら「今のバッファ自身」を検索していることになります。検索パターンは通常の検索と同じくVimの正規表現が使えます。
vimgrepではなくvimgrepaddを使う
ここが要注意です。:vimgrepは実行するたびにQuickfixリストを作り直します。:bufdoで回すと最後のバッファの結果だけが残り、それ以前のバッファでのヒットは消えてしまいます。手元のVim 9.1で、3つのファイル(1つ目に1件、2つ目に0件、3つ目に2件)を開いて確かめました。
| コマンド | Quickfixに残った件数 |
|---|---|
bufdo vimgrep | 2件(3つ目のファイルの分だけ) |
bufdo vimgrepadd | 3件(全ファイル分) |
追記していく:vimgrepaddを使い、開始前にsetqflist([], 'f')でリストを空にしておくのが正解です。
silent!が無いと途中で止まる
もう1つの落とし穴が、マッチしないバッファでの中断です。:vimgrep系はヒットが無いとエラーになり、:bufdoはそこで巡回をやめます。
E480: No match: TARGET
先ほどの3ファイルでsilent!を付けずに実行すると、2つ目のファイル(マッチ無し)で止まり、Quickfixの結果は0件でした。1つ目でヒットしていたにもかかわらずです。silent!を付けておけばエラーを無視して最後まで巡回します。
結果一覧を常に自動表示させる
毎回| cwindowを付けるのが面倒であれば、autocmdで自動化しておけます。
autocmd QuickFixCmdPost *grep* cwindow
設定ファイルに記載しておけば、grep系コマンドを実行するたびに自動でQuickfixウインドウが開くようになります。
コマンド化しておくと手早い
頻繁に使うなら、ユーザー定義コマンドとキーマッピングにしておくと入力の手間が減ります。
command! -nargs=1 Bufgrep
\ call setqflist([], 'f') |
\ silent! bufdo vimgrepadd /<args>/g % |
\ cwindow
nnoremap <Leader>g :Bufgrep<Space>
3ファイル(うち1つはマッチ無し)で試すと、全部で3件がQuickfixに並び、一覧ウインドウもそのまま開きました。1行にまとめる場合は|の扱いに注意が必要で、:bufdoに続く|は「bufdoで実行するコマンドの一部」と解釈されるため、上のようにsilent!とcwindowを分けて書くのが安全です。
要点:全バッファ検索は:silent! bufdo vimgrepadd /パターン/g %のあとに:cwindow。vimgrep(addなし)だと最後のバッファ以外の結果が消えます。
vimgrepが「内部grep」と呼ばれる理由
:vimgrepはVim標準機能として実装された検索で、内部grepとも呼ばれます。OSのgrepコマンドを呼び出す外部grepと比べて、次のような利点があります。
- 検索にヒットした件数がわかる
- Quickfixで一覧表示できる
- ファイルを開くときと同様に改行コードとエンコーディングが自動的に認識される
指定したファイル・ワイルドカードに対して検索したい場合は:bufdoを使わず、vimgrepで複数ファイルを直接検索する記事を参照してください。内部grepと外部grepの詳しい違いはこちらの比較記事にまとめています。
設定ファイルを分割して複数バッファで開いている状態で、特定のキーマッピングをどこに書いたか忘れることがよくあったが、bufdo vimgrepで一括検索するようになってからは探す手間がほぼゼロになりました。autocmdでcwindowを自動表示するようにしてからは、検索後の一手間も省けています。
まとめ
開いている全バッファを横断検索したいときは:silent! bufdo vimgrepadd /パターン/g %のあとに:cwindowです。よく紹介される:bufdo vimgrep ... | cwは、実際に試すと最後のバッファの結果しか残らず、マッチしないバッファがあるとそこで止まります。vimgrepaddで追記し、silent!でエラーを飛ばす。この2点さえ押さえれば、あとはユーザー定義コマンドにしておくと日常的に使えます。