Vimのvimgrepとgrepの違い(内部grepと外部grep)
Vimには全文検索コマンドが2種類あります。Vim標準機能の:vimgrep(内部grep)と、OSのgrepコマンドを呼び出す:grep(外部grep)です。名前が似ていて混同しやすいものの、性質はかなり異なります。そもそもどの範囲を探すのにどれを使うかは検索まとめで並べています。
目次(10項目)
| キー | これだけ覚える |
|---|---|
:vim {パターン} {対象} | 内部grep。環境非依存だが低速 |
:grep {パターン} {対象} | 外部grep。高速だが環境依存 |
set grepprg=internal | 外部grepを内部grepの動作に切り替え |
内部grep(vimgrep)
:vimgrep {検索パターン} {検索対象}
:vim {検索パターン} {検索対象}
Vim標準のExコマンドとして実装されています。Vimさえ動けばOSを問わず同じように使えますが、対象ファイルをすべてメモリに読み込みながら検索するため、動作速度は控えめです。
外部grep(grep)
:grep {検索パターン} {検索対象}
:gr {検索パターン} {検索対象}
OS側のgrepコマンド(Windowsならfindstr)をそのまま呼び出すExコマンドです。実行しているのは基本的に次のようなコマンドで、OSコマンドとしてのgrepの引数がそのまま使えます。
:!grep -n $* /dev/null {検索パターン} {検索対象}
内部grepより高速に動作するのが利点ですが、実行可否や挙動が環境(OSやシェル)に依存します。
比較まとめ
| 比較軸 | 内部grep(vimgrep) | 外部grep(grep) |
|---|---|---|
| 実行速度 | 普通 | 高速 |
| 検索対象 | ファイル(複数行パターン可) | ファイル(コマンド依存) |
| エンコーディング認識 | 自動 | 環境依存 |
| 結果の表示先 | Quickfix/locationウインドウ | Quickfix/locationウインドウ |
| ヒット件数の表示 | あり | あり |
使うコマンドを変更する
外部grepで実際に呼び出すコマンドはgrepprgオプションで指定されています。
set grepprg=grep\ -n\ $*\ /dev/null " Linux/Mac
set grepprg=findstr\ /n " Windows
逆に、外部grepの呼び出しそのものを内部grepの動作に切り替えることもできます。
set grepprg=internal
近年はripgrepのような高速な外部検索ツールをgrepprgに設定して使うケースが増えています。
set grepprg=rg\ --vimgrep\ --smart-case
set grepformat=%f:%l:%c:%m
ripgrepは.gitignoreを自動的に尊重して不要なファイルを除外してくれるため、大規模なリポジトリではvimgrep・標準grepよりも快適に使えることが多くなります。
具体的な検索コマンド例
複数ファイルを対象にした具体的な使い方はvimgrepで複数ファイルを検索する記事、開いているバッファだけを対象にしたい場合はbufdo + vimgrepの記事を参照してください。
数百ファイル規模のプロジェクトではvimgrepの速度に不満を感じることがあり、grepprgをripgrepに切り替えてから体感速度が大きく改善しました。設定ファイルのようなごく少数のファイルを検索する分にはvimgrepのままでも問題は感じていません。
grepprg を高速な検索ツールに差し替える
:grep が呼び出すコマンドは grepprg で決まります。ripgrepのような高速なツールを入れているなら、ここを差し替えるだけで :grep がそのまま速くなります。
set grepprg=rg\ --vimgrep\ --smart-case
set grepformat=%f:%l:%c:%m
grepformat は出力の読み取り方を指定するもので、ファイル名・行・桁・本文の並びを教えています。ここが合っていないと結果がquickfixに入らないので、grepprg だけ変えて動かないときはこちらを確かめてください。
ripgrepは .gitignore を自動で尊重するので、ビルド生成物や依存ライブラリを検索から外す設定を別途書かずに済みます。プロジェクト全体を対象にする検索ではこの差が大きく出ます。
結果の一覧は2種類ある
検索結果を入れる箱は、ウィンドウ全体で共有するquickfixリストと、ウィンドウごとに持つlocationリストの2つがあります。:vimgrep は前者、:lvimgrep は後者に入れます。
:vimgrep /pat/g **/*.js | copen " quickfix(共有)
:lvimgrep /pat/g **/*.js | lopen " location(そのウィンドウだけ)
2つの検索結果を並べて見比べたいときは、片方をlocationリストに入れておくと上書きされません。ビルドエラーをquickfixに残したまま別の検索をしたい、という場面でも効きます。
最初の一致に飛ばせない
:vimgrep は既定で最初の一致箇所へカーソルを飛ばします。一覧だけ見たいのに勝手に移動されると邪魔なので、j フラグを付けると飛ばなくなります。
:vimgrep /TODO/gj **/*.py | copen
パターンの後ろの g は「1行に複数あれば全部拾う」、j は「飛ばない」という意味です。この2つを付けた形を基本にしておくと、検索したあとに元の場所へ戻る手間がなくなります。
まとめ
普段使いなら環境非依存で扱いやすいvimgrep、大規模な検索が必要ならgrepprgを高速な外部ツールに切り替えるのがおすすめです。プロジェクトの規模に応じて使い分けると便利です。