我正在寻找在 Vim 中的所有项目文件中进行搜索和替换(确认)的最佳方法。 “项目文件”是指当前目录中的文件,其中一些不必打开。
一种方法是简单地打开当前目录中的所有文件:
:args ./**
然后对所有打开的文件进行搜索和替换:
:argdo %s/Search/Replace/gce
但是,当我这样做时,Vim 的内存使用量从几十 MB 跃升至超过 2 GB,这对我来说不起作用。
我还安装了 EasyGrep 插件,但它几乎从不工作 - 要么找不到所有匹配项,要么在我按下 CtrlC 之前一直挂起。到目前为止,我完成这项任务的首选方式是ack-grep作为搜索词,使用它的快速修复窗口打开任何包含该词且之前未打开的文件,最后是:bufdo %s/Search/Replace/gce
。
我正在寻找一个可以用于此目的的良好、有效的插件,或者一个命令/命令序列,它比我现在使用的更容易。
这里的另一个重要选择就是不使用 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/'
等等!
编辑:使用 cfdo
命令而不是 cdo
来显着减少完成此操作所需运行的命令数量(因为 cdo
在每个元素上运行命令,而 cfdo
在每个元素上运行命令每个文件)
感谢最近添加的 cdo
command,您现在可以使用已安装的任何 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
命令填充的 quickfix 列表中的每个术语执行给定的命令。)
(如果要替换每个搜索词而不每次都确认,请删除第二条命令末尾的 c
)
:cdo update
以保存所有文件中的更改。
c
(只需使用 g
)让它搜索和替换而不要求确认
update
,我必须:cdo %s/<search term>/<replace term>/g | update
我决定使用 ack 和 Perl 来解决这个问题,以便利用更强大的完整 Perl 正则表达式而不是 GNU 子集。
ack -l 'pattern' | xargs perl -pi -E 's/pattern/replacement/g'
解释
确认
ack 是一个很棒的命令行工具,它混合了 grep
、find
和完整的 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/。
Greplace 适合我。
github 上还有一个 pathogen ready version。
:Gsearch
和 :Gbuffersearch
等其他命令存在,但是当我输入 :Greplace
时,我得到 Not an editor command: Greplace
。
:Gsearch [<grep-option(s)>] [[<pattern>] [<filename(s)>]]
,因此您可以使用例如::Gsearch -r pattern dir/
进行递归搜索
也许这样做:
:noautocmd vim /Search/ **/*
:set hidden
:cfirst
qa
:%s//Replace/gce
:cnf
q
1000@a
:wa
解释:
:noautocmd vim /Search/ **/* ⇒ 在 cwd 的所有子目录中的所有文件中查找(vim 是 vimgrep 的缩写)模式,而不触发 autocmds (:noautocmd),为了速度。
:set hidden ⇒ 允许修改后的缓冲区不在窗口中显示(可能在你的 vimrc 中)
:cfirst ⇒ 跳转到第一个搜索结果
qa ⇒ 开始将宏记录到寄存器 a
:%s//Replace/gce ⇒ 将所有出现的最后一个搜索模式(当时仍然是 /Search/)替换为 Replace:在同一行(g 标志)多次使用用户确认(c 标志),如果没有则没有错误找到模式(e 标志)
在同一行上多次(g 标志)
带有用户确认(c 标志)
如果没有找到模式,则没有错误(e 标志)
:cnf ⇒ 跳转到 vim 命令创建的列表中的下一个文件
⇒ 停止录制宏
1000@a ⇒ 播放存储在寄存器 a 中的宏 1000 次
:wa ⇒ 保存所有修改过的缓冲区
编辑 * Vim 8 方式:
从 Vim 8 开始,有一种更好的方法可以做到这一点,因为 :cfdo
会迭代 quickfix 列表中的所有文件:
:noautocmd vim /Search/ **/*
:set hidden
:cfdo %s//Replace/gce
:wa
vim
在很多方面赢得了我的青睐,但我想我会坚持使用 PowerGrep。
从 shell 命令填充 :args
可以(在某些操作系统上1))通过 shell 命令为 :args
提供文件。
例如,如果您安装了 ack2,
:args `ack -l pattern`
将要求 ack 返回包含“模式”的文件列表并将它们放在参数列表中。
或者用普通的 grep 我猜它会是:
:args `grep -lr pattern .`
然后您可以按照 OP 的描述使用 :argdo
:
:argdo %s/pattern/replacement/gce
从 quickfix 列表中填充 :args
另请查看 nelstrom's answer 的相关问题,该问题描述了一个简单的用户定义命令,该命令从当前快速修复列表中填充 arglist。这适用于许多命令和插件,它们的输出最终出现在 quickfix 列表中(:vimgrep
、:Ack
3、:Ggrep
4)。
然后可以通过以下方式完成执行项目范围搜索的顺序:
:vimgrep /pattern/ **/*
:Qargs
:argdo %s/findme/replacement/gc
其中 :Qargs
是对从 quickfix 列表填充 arglist 的用户定义命令的调用。
您还可以在 ensuing discussion 中找到简单插件的链接,这些插件可以将此工作流程缩减为 2 或 3 个命令。
链接
:h {arglist} - vimdoc.sourceforge.net/htmldoc/editing.html#{arglist} ack - betterthangrep.com/ack.vim - github.com/mileszs/ack.vim 逃犯 - github.com/tpope/vim-逃犯
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
对 quickfix 列表中的每个文件运行以下命令。输入 :help cfdo
了解详细信息。
s/<search term>/<replace term>/g
替换每个术语。 /g
表示替换文件中的每个匹配项。
| update
在每次替换后保存文件。
我根据 this answer 及其评论将其拼凑在一起,但我觉得它应该得到自己的答案,因为它都在一个地方。
2. :cfdo %s/<search term>/<replace term>/g | update
没有 %
,只有第一次出现的模式被替换。
%s
也适用于常规 vim。
如果您不介意引入外部依赖,我制作了一个插件 ctrlsf.vim(依赖于 ack 或 ag)来完成这项工作。
它可以格式化和显示来自 ack/ag 的搜索结果,并将结果缓冲区中的更改同步到磁盘上的实际文件。
也许下面的演示会解释更多
https://i.stack.imgur.com/pkk79.gif
确保您使用的是 Neovim(或 Vim 7.4.8+,但实际上只使用 Neovim) 为命令行安装 FZF 并作为 vim 插件安装 Ag,以便在 vim 中自动对 FZF 可用 如果在 OSX 上使用 iTerm2,将 alt/option 键设置为 Esc+
用法
在当前目录中搜索要更改的文本及其子目录
:Ag text
继续输入模糊过滤器项目
使用 alt-a 选择项目
使用 alt-d 取消选择项目
Enter 将填充快速修复列表
:cfdo %s/text/newText/g | :w
现在你已经在 Vim NeoVim 中制作了 chabges
你可以用 shell 和 vim 的 ex-mode 来做。这具有不需要记住其他搜索和替换转义序列的额外好处。
任何可以列出文件的命令都可以使用(rg -l
、grep -rl
、fd
...)。例如在 bash 中:
for f in $(rg -l Search); do
vim -Nes "$f" <<EOF
%s/Search/Replace/g
wq
EOF
done
您可以在命令模式下使用任何以 :
为前缀的命令,就像在 vim 中一样,只需在开头删除 :
基本上,我希望在单个命令中替换,更重要的是在 vim 本身中
基于 answer by @Jefromi 我创建了一个键盘快捷键,我在我的 .vimrc 文件中设置了这样的
nmap <leader>r :!grep -r -l * \| xargs sed -i -e 's///g'
现在从vim,按leader+r,我在vim中加载了命令,我编辑如下,
:!grep -r -l <find> <file pattern> | xargs sed -i -e 's/<find>/<replace>/g'
因此,我使用单个命令进行替换,更重要的是在 vim 本身内
我认为这是因为您在内存中捕获了大量文件。就我而言,我通过对不同类型的文件进行分组来执行此操作,例如:
:args **/*.md
argdo <command>
:args **/*.haml
argdo <command>
sed
正则表达式,它们本质上是 POSIX.2 基本正则表达式,但不完全是(这在手册页中)-它们让\n
匹配换行符和其他一些类似的东西。因此它们与grep
正则表达式几乎相同。find . -type f | LANG=C xargs sed -i '' 's/SEARCH/REPLACE/g'