在 Vim 中跨所有项目文件搜索和替换

我正在寻找在 Vim 中跨所有项目文件进行搜索和替换(并进行确认)的最佳方法。我所说的“项目文件”是指工作目录中的文件,其中一些文件不必打开。

一种方法是简单地打开工作目录中的所有文件:

:args ./**

然后搜索并替换所有打开的文件:

:argdo %s/Search/Replace/gce

但是,当我这样做时,Vim 的内存使用量从几十 MB 跳到超过2GB,这对我来说不起作用。

我也有 EasyGrep插件安装,但它几乎从来没有工作 & mash; 要么它没有找到所有的出现,或者它只是挂起,直到我按 CtrlC。到目前为止,我首选的方式来完成这个任务,它对 Ak-grep的搜索词,使用它的快速修复窗口打开任何文件,包含这个词,没有打开之前,最后是 :bufdo %s/Search/Replace/gce

我正在寻找一个好的,工作的插件,可以用于这一点,或者一个命令/命令序列,将更容易比我现在使用的一个。

51722 次浏览

也许这样做:

:noautocmd vim /Search/ **/*
:set hidden
:cfirst
qa
:%s//Replace/gce
:cnf
q
1000@a
:wa

说明:

  • 为了速度起见,在 cwd 的所有子目录中的所有文件中进行 :noautocmd vim /Search/ **/*查找(vimvimgrep的缩写)而不触发 autocmds (:noautocmd)。
  • 允许在窗口中不显示修改后的缓冲区(可能在 vimrc 中)
  • 跳转到第一个搜索结果
  • 开始将一个宏记录到寄存器
  • Replace替换上次搜索模式(当时仍然是 /Search/)的所有出现:
    • 在同一行上多次(g标志)
    • 用户确认(c标志)
    • 如果没有发现模式(e标志)则没有错误
  • 跳转到由 vim命令创建的列表中的下一个文件
  • 停止录制宏
  • 播放存储在寄存器中的宏1000次
  • 保存所有修改后的缓冲区

活力八道:

从 Vim 8开始有一个更好的方法,因为 :cfdo迭代快速修复列表中的所有文件:

:noautocmd vim /Search/ **/*
:set hidden
:cfdo %s//Replace/gce
:wa

另一个重要的选择就是不要使用 vim:

sed -i 's/pattern/replacement/' <files>

或者,如果你有一些方法来生成一个文件列表,也许像这样的东西:

find . -name *.cpp | xargs sed -i 's/pattern/replacement/'
grep -rl 'pattern1' | xargs sed -i 's/pattern2/replacement/'

等等!

我决定使用 ack 和 Perl 来解决这个问题,以便利用更强大的完整 Perl 正则表达式,而不是 GNU 子集。

ack -l 'pattern' | xargs perl -pi -E 's/pattern/replacement/g'

解释

Ack 是一个非常棒的命令行工具,它混合了 grepfind和完整的 Perl 正则表达式(不仅仅是 GNU 子集)。它使用纯 Perl 语言编写,速度快,语法突显好,可以在 Windows 上运行,对程序员来说比传统的命令行工具更加友好。用 sudo apt-get install ack-grep在 Ubuntu 上安装它。

Xargs

Xargs 是一个旧的 unix 命令行工具。它从标准输入中读取项,并执行指定的命令,然后执行为标准输入所读取的项。因此,从根本上说,ack 生成的文件列表被追加到 perl -pi -E 's/pattern/replacemnt/g'命令的末尾。

Perl-pi

Perl 是一种编程语言。P 选项使 Perl 围绕您的程序创建一个循环,循环遍历文件名参数。I 选项使 Perl 就地编辑文件。您可以修改它来创建备份。E 选项使 Perl 执行指定为程序的一行代码。在我们的示例中,该程序只是一个 Perl 正则表达式替换。有关 Perl 命令行选项 perldoc perlrun的详细信息,请参阅。有关 Perl 的更多信息,请参见 http://www.perl.org/

通过 shell 命令填充 :args

(在某些操作系统 1上)可以通过 shell 命令为 :args提供文件。

例如,如果安装了 ack2,

:args `ack -l pattern`

将要求 ack 返回一个包含“ pattern”的文件列表,并将这些文件放在参数列表中。

或者我猜就是:

:args `grep -lr pattern .`


然后,您可以按照 OP 所描述的那样使用 :argdo:

:argdo %s/pattern/replacement/gce


从快速修复列表中填充 :args

还可以查看 Nelstrom 的回答中的一个相关问题,该问题描述了一个用户定义的简单命令,用于填充当前快速修复列表中的 arglist。这对于许多命令和插件非常有效,它们的输出最终会出现在快速修复列表中(:vimgrep:Ack3:Ggrep4)。

在项目范围内进行搜索的顺序可以通过以下方式完成:

:vimgrep /pattern/ **/*
:Qargs
:argdo %s/findme/replacement/gc

其中 :Qargs是对用户定义的命令的调用,该命令从快速修复列表中填充 arglist。

您还可以在 随后的讨论中找到指向简单插件的链接,这些插件可以将此工作流程简化为2或3个命令。

连结

  1. : h { arglist } - vimdoc.sourceforge.net/htmldoc/editing.html# { arglist }
  2. 返回文章页面 betterthangrep.com/
  3. 返回文章页面 github.com/mileszs/ack.vim
  4. Github.com/tpope/vim-fugitive

基本上,我希望在单个命令中替换,更重要的是在 vim 本身中替换

基于 作者:@Jefromi,我创建了一个快捷键,像这样设置在我的.vimrc 文件中

nmap <leader>r :!grep -r -l  * \| xargs sed -i -e 's///g'

现在,从 vim,在一个笔触的领导者 + r 我得到的命令加载在 vim,我编辑如下,

:!grep -r -l <find> <file pattern> | xargs sed -i -e 's/<find>/<replace>/g'

因此,我使用单个命令替换,更重要的是在 vim 本身内部替换

如果你不介意引入外部依赖,我已经开发了一个插件 Ctrlsf.vim(取决于 Ag)来完成这项工作。

它可以格式化和显示来自 ack/ag 的搜索结果,并将结果缓冲区中的更改与磁盘上的实际文件同步。

也许下面的演示会解释更多

ctrlsf_edit_demo

编辑: 使用 cfdo命令而不是 cdo命令可以显著减少为完成 而运行的命令数量(因为 cdo在每个元素上运行命令,而 cfdo在每个文件上运行命令)

感谢最近添加的 cdo指令,您现在可以用两个简单的命令使用您已经安装的 grep工具来完成这项工作。不需要额外的插件!:

1. :grep <search term>
2. :cdo %s/<search term>/<replace term>/gc
3. (If you want to save the changes in all files) :cdo update

(cdo对快速修复列表中的每个项执行给定的命令,grep命令将填充该列表。)

(如果你想替换每个搜索词而不需要每次都确认,请在第2条命令的末尾删除 c)

2016年还有一个选择,Far.vim插件:

far.vom

1. :grep <search term> (or whatever you use to populate the quickfix window)
2. :cfdo %s/<search term>/<replace term>/g | update

步骤1用您想要的项目填充快速修复列表。在这种情况下,它使用您希望通过 grep更改的搜索词进行传播。

cfdo对快速修复列表中的每个文件运行以下命令。

s/<search term>/<replace term>/g代替每个术语。 /g意味着代替文件中的每个事件。

| update在每次替换后保存文件。

我根据 这个答案和它的评论拼凑了这个问题,但是觉得它应该有自己的答案,因为它们都在一个地方。

  1. 确保您正在使用 Neovim (或者 Vim 7.4.8 + ,但实际上只是使用 Neovim)
  2. 为命令行安装 FZF 并将其作为 vim 插件
  3. 安装 Ag,这样 Vim 中的 FZF 就可以自动使用它了
  4. 如果在 OSX 上使用 iTerm2,请将 alt/option 键设置为 Esc +

用法

在工作目录中搜索要更改的文本及其子元素

:Ag text

  • 继续输入模糊过滤器项
  • 使用 alt-a 选择项
  • 使用 alt-d 取消选择项
  • 输入将填充快速修复列表
  • : cfdo% s/text/newText/g | : w

现在你在 Vim NeoVim 体内做了电击

来源

你可以用 shell 和 Vim 的前模式。这样做的另一个好处是不需要记住其他搜索和替换转义序列。

任何可以列出文件的命令都可以工作(rg -lgrep -rlfd...):

for f in $(rg -l Search); do
vim -Nes "$f" <<EOF
%s/Search/Replace/g
wq
EOF
done

您可以使用任何命令,那些在命令模式中以 :为前缀的命令,与您在 vim 中使用的方法相同,只需在开始时删除 :

我认为这是因为你有大量的文件被捕获在内存中。对于我的情况,我通过不同类型的组文件来完成这项工作,例如:

:args **/*.md
argdo <command>


:args **/*.haml
argdo <command>