在 Vim 中使用 quickfix 列表搜索和替换

Posted

技术标签:

【中文标题】在 Vim 中使用 quickfix 列表搜索和替换【英文标题】:Search & replace using quickfix list in Vim 【发布时间】:2011-08-06 20:54:18 【问题描述】:

到目前为止,我一直使用 EasyGrep 来替换多个文件中的文本。不幸的是,当项目变得更大时,它会很慢。似乎非常快的一件事是 fugitive.vim 的 Ggrep,它只搜索我的版本控制文件。所有结果也存储在 quickfix 列表中。

如何使用 Ggrep 的结果对所有找到的文件进行简单替换?是否有可能在快速修复列表中的所有文件上使用%s/foo/bar/cg,或者有更好的方法吗?

【问题讨论】:

【参考方案1】:

更新: Vim 现在有cdo,见Sid's answer。

原答案:

Vim 有bufdowindotabdoargdo,可以让你在参数列表中所有打开的缓冲区、窗口或文件中执行相同的命令。我们真正需要的是像quickfixdo 这样的东西,它会在quickfix 列表中的每个文件上调用一个命令。遗憾的是,Vim 缺少该功能,但 here's a solution by Al 提供了一个自制的解决方案。使用它,可以运行:

:QFDo %s/foo/bar/gc

这将在 quickfix 列表中的所有文件上运行 foo/bar 替换。

bufdowindotabdoargdo 命令有一些共同的行为。例如,如果当前文件不能被放弃,那么所有这些命令都会失败。我不确定上面引用的QFDo 命令是否遵循相同的约定。

我已经修改了Al's solution 来创建一个名为Qargs 的命令。运行此命令会使用 quickfix 列表中列出的所有文件填充参数列表:

command! -nargs=0 -bar Qargs execute 'args ' . QuickfixFilenames()
function! QuickfixFilenames()
  " Building a hash ensures we get each buffer only once
  let buffer_numbers = 
  for quickfix_item in getqflist()
    let buffer_numbers[quickfix_item['bufnr']] = bufname(quickfix_item['bufnr'])
  endfor
  return join(values(buffer_numbers))
endfunction

使用它,您可以按照以下步骤进行项目范围的搜索和替换:

:Ggrep findme
:Qargs
:argdo %s/findme/replacement/gc
:argdo update

编辑:(向 Peter Rincker 致敬)

或者您可以将最后 3 个命令合并到一行中:

:Ggrep findme
:Qargs | argdo %s/findme/replacement/gc | update

【讨论】:

我相信你应该在你的Qargs 命令中使用-nargs=0-bar 使用argsargdo 是个好主意!我认为将Qargsargdo 命令合二为一会更好(也更方便):command! -nargs=1 -complete=command -bang Qargdo exe 'args '.QuickfixFilenames() | argdo<bang> <args> 我创建了一个简单的插件来包装:Qargs 命令:github.com/nelstrom/vim-qargs @ib。谢谢你(当然还有@nelstrom)。分叉 @nelstrom 的插件以添加您的 Qargdo,但作为 Qdo 以减少命令完成冲突:github.com/henrik/vim-qargs 一个补丁 was submitted 将 :cdo 和 :ldo 命令添加到 Vim。【参考方案2】:

cdo 命令现已添加! grep 后,您可以使用cdo 对您的快速修复列表中的每个术语执行给定的命令:

cdo %s/<search term>/<replace term>/cg

(查看git commit 和vim developers google group discussion,了解有关cdo 的更多信息以及添加它的动机。)

【讨论】:

我认为 replace 命令不应包含 % 以防止替换所选列表之外的行。如果替换不是幂等的,也可能导致意外结果。 使用cdo 还为更灵活的操作提供了惊人的选项。例如cdo norm capMy new paragraph 这个例子在每次出现搜索词时运行normal,将搜索词周围的整个段落更改为“我的新段落”。惊人的强大。 我还需要将上面的内容传递给“update”命令,否则 vim 不想移动到下一个文件,因为缓冲区已被编辑但未保存。命令:cdo %s/<search term>/<replace term>/gc | update【参考方案3】:

nelstrom 的回答相当全面,反映了他对 vimdom 的杰出贡献。它也有点超出了这里的严格要求;可以省略快速修复步骤,以便使用 shell 命令的结果填充 args:

:args `git grep -l findme`
:argdo %s/findme/replacement/gc
:argdo update

应该是你所需要的。

编辑:正如 Domon 所说, :set hidden 如果尚未设置,则必须先完成!

【讨论】:

+1 务实的解决方案。基于此,您还可以在启动 vim 时填充 arglist:vim `git grep -l findme` 这对我有用。但是,我必须在 :argo ... 之前 :set hidden 否则 Vim 会因为未保存的更改而出错。【参考方案4】:

外部grep

(使用 grepprg、grepformat 就像在 makeprg/errorformat 中一样;如果 grepprg=='internal' 这与 internal grep 相同)

:grep fopen *.c
:copen
:cnext

内部grep

:vimgrep /\<myVimregexp\>/ **/*.c
:copen
:cnext

等等

位置列表内部 grep

:lvimgrep /\<myVimregexp\>/ **/*.c
:lopen
:lnext

等等

奖励:为加载的缓冲区执行外部 grep:

:silent bufdo grepadd fstream %
:copen
:cnext

等等

所有参数的外部参数:

:silent argdo grepadd fstream %
:copen
:cnext

【讨论】:

不确定您要通过这些示例指出什么。我知道如何使用 grep 和 vimgrep。我不明白这个答案如何适合这个问题。 (a) 向您展示一些减少 vim 中 grep 范围的方法 (b) 向您展示如何为外部 grep 做这些事情。如果有点无聊,我很抱歉,但它对我有用:)【参考方案5】:

使用quickfix-reflector.vim,您可以在快速修复窗口中编辑您的搜索结果。然后write 命令会将更改保存到您的文件中。

:copen
:%s/foo/bar/cg
:write

【讨论】:

【参考方案6】:

有一个补丁可以将cdo(Quickfix do)命令添加到vim,但是还没有拉出来(截至2015-03-25):

https://groups.google.com/forum/#!topic/vim_dev/dfyt-G6SMec

你可能想自己给 vim 打补丁来获得这个补丁:

brew install hg # install mercurial, e.g. with homebrew
hg clone https://vim.googlecode.com/hg/ vim
cd vim
# copy/download patch to . folder
patch -b -p1 < cdo.diff
./configure
make && make install

【讨论】:

以上是关于在 Vim 中使用 quickfix 列表搜索和替换的主要内容,如果未能解决你的问题,请参考以下文章

使用 ninja 和 clang++ 时 vim 中的快速修复列表

从零开始配置vim(20)——模糊查询

从零开始配置vim(20)——模糊查询

vim+makefile入门编辑,编译,差错实例

vim通过函数

在 Visual Studio 2010 (utility.obj) 中使用带有 VC++ 的 QuickFix 时出错