簡介
:vimgrep是Vim提供的「直接在指定文件集裏用正則查找」的命令- 與外部
grep不同,vimgrep在查到結果後會將匹配行寫入 快速修復列表(quickfix list),並可通過:copen、:cnext、:cfirst等命令逐條跳轉 - 支持
Vim的正則引擎,允許靈活使用Vim正則、分組、魔法模式等
基本語法
:vimgrep[!] /{pattern}/[g][j] {file_pattern}[ ...]
!:表示清空當前quickfix列表;不帶!時會追加到現有列表/…/:Vim正則模式,若包含/,可改用其它分隔符,如#…#g:對匹配到多處的行,依然只在列表中記錄一次;不加g時同一行多次匹配會重複記錄j:匹配後不跳轉到第一個結果,僅更新列表不移動光標{file_pattern}:文件通配符,支持*,**(遞歸)、?等
例 1:搜索當前目錄下所有 .c 文件中包含 “TODO” 的行
:vimgrep /TODO/ *.c
- 執行後,打開
quickfix列表:
:copen
- 跳到下一個匹配:
:cnext
- 跳到上一個匹配:
:cprevious
文件搜索範圍示例
| 場景 | 命令示例 | 説明 |
|---|---|---|
| 當前文件 | :vimgrep /error/ % |
% 表示當前緩衝區 |
| 所有文本文件 | :vimgrep /TODO/ *.txt |
當前目錄下 .txt 文件 |
| 遞歸搜索子目錄 | :vimgrep /function_name/ **/*.py |
所有 Python 文件 |
| 多個文件 | :vimgrep /pattern/ file1.txt file2.txt |
多個文件 |
| 多類型文件組合搜索 | :vimgrep /pattern/ **/*.cpp **/*.h |
C++ 頭文件和源文件 |
| 所有緩衝區 | :vimgrep /pattern/ ## |
搜索所有打開的緩衝區 |
核心用法示例
基礎搜索(當前目錄指定文件)
:vimgrep "error" *.log " 在當前目錄所有.log文件中搜索"error"
遞歸搜索(包含子目錄)
使用 ** 匹配所有子目錄:
:vimgrep "func"**/*.py " 在所有子目錄的.py文件中搜索"func"
正則表達式搜索
支持 Vim 風格正則(如 \d 匹配數字,\w 匹配單詞字符):
:vimgrep "age=\d+" **/*.html " 搜索所有html中"age=數字"的模式
區分大小寫 / 忽略大小寫
- 默認區分大小寫
- 加
i參數忽略大小寫:
:vimgrep /Error/ig **/*.js " i = 忽略大小寫,g = 全局匹配(每個文件找所有匹配)
結合快速修復列表(Quickfix)
- 打開列表
:copen " 在窗口下方打開 quickfix 窗口
:cclose " 關閉 quickfix 窗口
- 在列表中跳轉
:cfirst " 跳到第一個匹配
:clast " 跳到最後一個匹配
:cnext " 下一個
:cprevious" 上一個
:cc [nr] " 跳到第 nr 條(不帶 nr 跳到下一條)
- 在列表中執行批量操作
比如對所有匹配行做修改,可配合 :cfdo
:cfdo s/old/new/ge | update
這會對 quickfix 中的每個文件執行替換並保存。
- 快捷鍵映射(加入
~/.vimrc提升效率):
nnoremap <F8> :copen<CR> " F8 打開 Quickfix
nnoremap <F9> :cclose<CR> " F9 關閉
nnoremap <F10> :cnext<CR> " F10 下一項
nnoremap <F11> :cprev<CR> " F11 上一項
常用選項和技巧
- 只列出文件名
:vimgrep /pattern/ **/*.py | cw
然後在 quickfix 窗口裏可以只關注文件名列。
- 使用非常規分隔符
當搜索模式裏包含 / 時,用其它符號:
:vimgrep #{foo/bar}# **/*.js
- 只更新列表、不跳轉
:vimgrep j /TODO/ **/*.java
搜索後光標不動,便於接着做別的。
- 區分大小寫
Vim 默認受 ignorecase 和 smartcase 影響,你可臨時加 \C 強制大小寫敏感,或 \c 強制忽略大小寫:
:vimgrep /\CError/ **/*.log
- 與
:args/argdo配合
先加載參數列表:
:args **/*.md
再 vimgrep 並跳轉:
:vimgrep /TODO/ % | copen
高級技巧與性能優化
結合通配符精確篩選文件
:vimgrep "config" **/*.{json,yml} " 搜索所有子目錄的.json和.yml文件
將結果保存到文件
:vimgrep "todo"**/*.php | :cwrite todo_list.txt " 搜索結果保存到todo_list.txt
批量替換搜索結果
結合 cdo 命令(對快速修復列表中的每個文件執行命令):
:vimgrep "old_str" **/*.py " 先搜索目標字符串
:cdo %s/old_str/new_str/g | w " 對所有匹配文件執行替換並保存
自定義快捷鍵
在 .vimrc 中配置快捷鍵,簡化操作:
nnoremap <leader>vg :vimgrep // **/*.<C-R>=expand('%:e')<CR><Left><Left>
按 <leader>vg 後,自動填充當前文件類型的搜索模板,直接輸入關鍵詞即可
限制搜索範圍
" 只在修改過的文件中搜索
:vimgrep /warning/ `bufname()` " 當前緩衝區
:vimgrep /bug/ `expand('%:p:h')/*.c` " 當前目錄
性能優化
" 忽略大目錄(如 node_modules)
:set wildignore+=*/node_modules/*
:vimgrep /pattern/ **/*.js " 自動跳過忽略目錄
正則表達式轉義
Vim 的正則語法與 Perl/PCRE 不同,需注意 \v(非常魔術模式)簡化語法。
:vimgrep /\v<word>/ *.txt
多文件編輯:
結合 :argdo 或 :bufdo 進行批量操作
:argadd *.c
:argdo vimgrepadd /TODO/ % | cdo s/TODO/DONE/g | update
添加全局忽略配置
修改 .vimrc 文件
set wildignore+=*.o,*.obj,.git
lvimgrep 替代方案
使用 :lvimgrep 將結果存入 Location List(窗口局部列表),適用於多窗口並行搜索:
:lvimgrep /warning/ **/*.log
:lopen " 打開 Location List
:lnext
加速 vimgrep 性能
- 問題:
vimgrep需模擬打開每個文件,大項目較慢 -
優化:
- 使用
:noautocmd跳過插件/事件干擾:
:noautocmd vimgrep /pattern/ **/*.js- 換用外部
grep(需配置):
:set grepprg=grep\ -nri\ --include=*.{c,h} 定義外部 grep 參數 :grep "function_name" . 調用外部 grep - 使用
區別::vimgrep vs :grep
:vimgrep |
:grep |
|
|---|---|---|
| 搜索引擎 | Vim 自帶正則 | 調用外部程序(如 grep、ack、rg 等) |
| 性能 | 較慢(對小項目足夠) | 快(尤其用 ripgrep 等現代搜索工具) |
| 快速修復列表 | 默認輸出到 quickfix | 需 :grepprg 設置或 :make 設置才可輸出 |
| 正則語法 | Vim 正則(魔法模式、分組語法不同) | 外部工具的正則(POSIX / PCRE / Rust regex 等) |
如果更喜歡 rg、ag 的速度並想結合 quickfix,只需在 .vimrc 中:
set grepprg=rg\ --vimgrep\ --no-heading\ --smart-case
set grepformat=%f:%l:%c:%m
這樣用 :grep 就等同於 :vimgrep 加速版。
進階:批量替換與腳本化
全項目批量替換
:vimgrep /foo()/ **/*.cpp
:cfdo %s/foo()/bar()/g | update
在 Vim 腳本中使用
function! ReplaceInProject(pat, rep)
execute 'vimgrep /'.a:pat.'/j **/*.js'
copen
execute 'cfdo %s/'.a:pat.'/'.a:rep.'/g | update'
cclose
endfunction
" 用法:
:call ReplaceInProject('oldFunc', 'newFunc')
實戰示例
案例 1:項目級重構
" 查找所有使用舊 API 的地方
:vimgrep /\<old_api_call\>/ **/*.c
:copen " 查看結果並手動驗證
:cfdo %s/old_api_call/new_api_call/g | update
案例 2:日誌分析
" 在所有日誌中提取 ERROR 行
:vimgrep /^$$ERROR$$/ /var/log/*.log
:copen
" 在 quickfix 窗口按 : 輸入:
:cfdo g/^$$ERROR$$/yank A | put! A 將所有錯誤複製到新緩衝區