動態

詳情 返回 返回

Linux vimgrep 詳解 - 動態 詳情

簡介

  • :vimgrepVim 提供的「直接在指定文件集裏用正則查找」的命令
  • 與外部 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 默認受 ignorecasesmartcase 影響,你可臨時加 \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 將所有錯誤複製到新緩衝區
user avatar zero_dev 頭像 guangmingleiluodebaomihua 頭像 jianshendemifan 頭像 wangjingyu_5f58472234cff 頭像 jkdataapi 頭像 lywlinux 頭像 amc 頭像
點贊 7 用戶, 點贊了這篇動態!
點贊

Add a new 評論

Some HTML is okay.