Vimで複数ファイルを横断検索する(vimgrep + cwindow)
プロジェクト内の複数ファイルから特定のキーワードを探したいとき、いちいちファイルを開いてから検索する必要はありません。:vimgrepにワイルドカードでファイルを指定すれば、開いてすらいないファイルもまとめて検索できます。
目次(9項目)
基本の書式
:vimgrep /{検索パターン}/g {検索対象ファイル} | cwindow
:vim /{検索パターン}/g {検索対象ファイル} | cw
検索対象ファイルにはワイルドカードが使えます。例えば次のように指定できます。
:vim /numpy/g *.py | cw
:vim /autocmd/g **/*.vim | cw
:vim /structure/g ~/src/*.c | cw
**を使うと、カレントディレクトリ配下を再帰的に検索できます。1つ目の例はカレントディレクトリのPythonファイルからnumpyという文字列を、2つ目はカレントディレクトリ配下すべてのvimファイルからautocmdという文字列を検索します。
**を使うとサブディレクトリまで再帰的に検索できます。開いていないファイルも対象にできる点が:bufdoとの違いです。
:vimgrep /TODO/gj **/*.js のあと :copen 5 を実行した画面開いているバッファだけを検索したい場合
このコマンドはファイルパスを指定して検索するため、まだ開いていないファイルも対象にできるのが特徴です。逆に、すでに開いている複数バッファだけを対象にしたい場合はbufdo + vimgrepを使う記事を参照してください。使い分けの目安はこうなります。
| 目的 | コマンド |
|---|---|
| 開いている全バッファを検索 | :bufdo vimgrep ... % | cw |
| ファイル・ディレクトリを指定して検索 | :vimgrep ... {file} | cw |
結果一覧を自動表示する
autocmd QuickFixCmdPost *grep* cwindow
設定ファイルに書いておけば、grep系コマンドの実行後に毎回自動でQuickfixウインドウが開くようになります。
大量ファイルを検索するときの注意
vimgrepは各ファイルを実際にVimの編集バッファとして読み込みながら検索するため、ファイル数が多いと外部のgrepコマンドより低速になります。数千ファイル規模のリポジトリ全体を検索したい場合は、OSのgrepを呼び出す外部grepのほうが向いていることもあります。両者の違いはvimgrepとgrepの違いの記事で詳しく比較しています。
設定ファイル一式をディレクトリごと検索したいとき、**/*.vimのような再帰ワイルドカードを覚えてから、ファイラーで1つずつ開いて探す作業がほぼなくなりました。数百ファイル規模のプロジェクトでは体感で少し待つこともありますが、日常的な設定ファイルの検索であれば十分な速度で使えています。
検索結果からまとめて置換する
quickfixに入ったファイルすべてに同じ処理を流せます。:cfdo は結果に含まれる各ファイルで1回ずつコマンドを実行するので、置換と保存をつなげると一括置換になります。
:vimgrep /oldName/gj **/*.js
:cfdo %s/oldName/newName/ge | update
e フラグは一致が無くてもエラーにしないという指定で、これが無いと途中で止まります。update は変更があったファイルだけを保存します。実行前に :copen で対象のファイルを目で確かめておくと安全です。
似たコマンドに :cdo があり、こちらはファイルではなく一致した1件ごとに実行します。行単位の処理をしたいときはこちらです。
検索から除外するファイルを決める
**/* で再帰的に検索すると、node_modules やビルド生成物まで舐めてしまいます。wildignore に書いたパターンは :vimgrep の対象からも外れます。
set wildignore+=*/node_modules/*,*/.git/*,*/dist/*,*.min.js
これを設定しておくと、ファイル名の補完からも同じものが消えます。プロジェクトごとに違うなら、ローカルの設定ファイルを読み込む仕組みを入れておくと使い回せます。
quickfixウィンドウの中でできること
:copen で開いた一覧は普通のバッファなので、検索して絞り込んだり、行を消したりできます。ただし行を消しても検索結果そのものは変わりません。
:cnext / :cprev " 次 / 前の一致へ
:cfirst / :clast " 最初 / 最後へ
:colder / :cnewer " 1つ前 / 後の検索結果に切り替える
:colder は見落とされがちですが便利です。Vimは過去10回ぶんの検索結果を保持しているので、別の検索をしたあとでも前の結果に戻れます。検索し直す必要がありません。
まとめ
開いていないファイルまで含めて横断検索したいときは:vimgrep /パターン/g ファイル指定 | cw。**の再帰ワイルドカードと組み合わせれば、プロジェクト全体からのキーワード検索も1コマンドで完結します。