ChatGPT解决这个技术问题 Extra ChatGPT

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

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

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

:args ./**

然后对所有打开的文件进行搜索和替换:

:argdo %s/Search/Replace/gce

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

我还安装了 EasyGrep 插件,但它几乎从不工作 - 要么找不到所有匹配项,要么在我按下 CtrlC 之前一直挂起。到目前为止,我完成这项任务的首选方式是ack-grep作为搜索词,使用它的快速修复窗口打开任何包含该词且之前未打开的文件,最后是:bufdo %s/Search/Replace/gce

我正在寻找一个可以用于此目的的良好、有效的插件,或者一个命令/命令序列,它比我现在使用的更容易。

@Cascabel 既然你写了这个评论,就有一个 vi.stackexchange.com 网站。

C
Cascabel

这里的另一个重要选择就是不使用 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/'

等等!


谢谢!我真的需要学习这个老派的命令行东西。我认为这是最好的答案。这些正则表达式是 perl 正则表达式还是 vim 正则表达式或其他某种正则表达式?
@EricJohnson:它们是... sed 正则表达式,它们本质上是 POSIX.2 基本正则表达式,但不完全是(这在手册页中)-它们让 \n 匹配换行符和其他一些类似的东西。因此它们与 grep 正则表达式几乎相同。
您在 sed 命令中有 -i 的原因吗?手册页说这是用于指定扩展名,但您似乎并没有实际指定一个。
好的,它实际上看起来像 's/pattern/replacement/' 是扩展名,我想我现在明白了。
在 Mac OS X 上,唯一对我有用的 sed 命令是:find . -type f | LANG=C xargs sed -i '' 's/SEARCH/REPLACE/g'
S
Sid

编辑:使用 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 以保存所有文件中的更改。
但是当当前缓冲区被修改时,它会在每次切换到下一个缓冲区之前暂停以警告未保存。
@Zhe 谢谢,我已将其添加到答案中; @lfree 我个人更喜欢确认每个更改,但您可以删除第二个命令末尾的 c(只需使用 g)让它搜索和替换而不要求确认
:cdo %s/search term/replace term/g 仅替换第一次出现,但不适用于我的 vim 中的第二次出现
为了使用 update,我必须:cdo %s/<search term>/<replace term>/g | update
E
Eric Johnson

我决定使用 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/


我喜欢这个答案,因为它甚至适用于 cygwin 的行尾。请注意,它确实在修改行的末尾添加了一个 \r,但是当使用 sed 时,它将替换每个换行符,使您的差异无法使用。 Perl 更理智一些,这是一个非常棒的搜索和替换单行代码。谢谢!
@andrew,我不确定你的情况到底出了什么问题。它会帮助你在你的正则表达式中使用 \R 而不是 \n 吗?请参阅 perldoc.perl.org/perlrebackslash.html#Misc 中的 \R 部分。也试试 perldoc.perl.org/perlre.html#Regular-Expressions。 Perl 是非常跨平台的,我相信有一种方法可以让你不以损坏的行结尾结束。
我的正则表达式中没有任何换行符,这些程序的 cygwin 版本会在每行修改的末尾自动添加 \r。我特别喜欢 perl 版本,因为它只修改它匹配的行,而 sed 会修改所有行,即使它们与正则表达式不匹配。
如果文件的路径包含空格怎么办?
n
nelstrom

Greplace 适合我。

github 上还有一个 pathogen ready version


安装它后,我看到 :Gsearch:Gbuffersearch 等其他命令存在,但是当我输入 :Greplace 时,我得到 Not an editor command: Greplace
根据文档,语法是::Gsearch [<grep-option(s)>] [[<pattern>] [<filename(s)>]],因此您可以使用例如::Gsearch -r pattern dir/ 进行递归搜索
我讨厌 Greplace 的一点是,如果模式在文件名中,Greplace 会失败,因为你最终也会在文件名中替换它。
6 年后这里没有什么不寻常的地方,但与 3 多年前的一些新答案 (github.com/yegappan/greplace) 相比,这个插件已经严重过时了。我可能会查看 Sid 的内置解决方案:stackoverflow.com/a/38004355/2224331(这不是我在另一个帐户上,只是巧合:P)
B
Benoit

也许这样做:

: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

您可能想为我们这些小凡人进一步解释一下。我不清楚您是否需要每次都执行此“序列”。我使用生锈的旧 Delphi 5 更快地做到这一点,所以我肯定错过了一些东西。
@Lieven:你说得对,没有评论这有点晦涩难懂。我会做出一些解释。
+1,但尽管 vim 在很多方面赢得了我的青睐,但我想我会坚持使用 PowerGrep。
感谢您的回答和对所有命令的解释,我非常感谢。我接受另一个答案,因为我更喜欢为此使用插件。
考虑到提出问题的人的水平与理解该答案所需的水平之间存在差异,我已将其标记下来,因为它会引起更多的混乱。
C
Community

从 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:Ack3:Ggrep4)。

然后可以通过以下方式完成执行项目范围搜索的顺序:

: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-逃犯


argdo 在第一个文件出现写入问题后停止
O
Oleg Khalidov

2016 年的另一种选择,far.vim 插件:

https://i.stack.imgur.com/bhyXE.gif


您还应该说您是该插件的作者 :)
K
Kyle Heironimus
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 及其评论将其拼凑在一起,但我觉得它应该得到自己的答案,因为它都在一个地方。


对于 NeoVim 上的我来说,需要在上面的第 2 行中稍作修改:2. :cfdo %s/<search term>/<replace term>/g | update 没有 %,只有第一次出现的模式被替换。
谢谢卡里姆。我更新了答案,因为 %s 也适用于常规 vim。
d
dyng

如果您不介意引入外部依赖,我制作了一个插件 ctrlsf.vim(依赖于 ackag)来完成这项工作。

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

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

https://i.stack.imgur.com/pkk79.gif


A
Andres Felipe

确保您使用的是 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

source


V
Voovs

你可以用 shell 和 vim 的 ex-mode 来做。这具有不需要记住其他搜索和替换转义序列的额外好处。

任何可以列出文件的命令都可以使用(rg -lgrep -rlfd...)。例如在 bash 中:

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

您可以在命令模式下使用任何以 : 为前缀的命令,就像在 vim 中一样,只需在开头删除 :


C
Community

基本上,我希望在单个命令中替换,更重要的是在 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 本身内


我实际上使用 ag 而不是 grep
h
hiveer

我认为这是因为您在内存中捕获了大量文件。就我而言,我通过对不同类型的文件进行分组来执行此操作,例如:

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

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