如何在vim中用ack进行搜索和替换?
Posted
技术标签:
【中文标题】如何在vim中用ack进行搜索和替换?【英文标题】:How to do search & replace with ack in vim? 【发布时间】:2011-06-15 02:54:29 【问题描述】:我在 Vim 中使用 Ack 插件,它可以帮助我在项目中快速搜索字符串。但是,有时我想替换所有或部分找到的字符串。您可以像这样使用 Vim arglist 进行某种全局搜索和替换 (source) :
:args app/views/*/*
:argdo %s/, :expire.*)/)/ge | update
但不是使用args
,我更愿意通过 Ack 进行搜索,然后在找到的所有文件中进行替换。有没有类似于argdo
命令的方法?
【问题讨论】:
【参考方案1】:我决定使用ack
和perl
在Vim 之外解决这个问题,这样我就可以使用更强大的Perl 正则表达式而不是GNU 子集。您可以将其映射到您的.vimrc
中的一个击键。
ack -l 'pattern' | xargs perl -pi -E 's/pattern/replacement/g'
说明
ack
ack 是一个很棒的命令行工具,它混合了grep
、find
和完整的 Perl 正则表达式(不仅仅是 GNU 子集)。它是用纯 Perl 编写的,速度快,具有匹配突出显示功能,可以在 Windows 上运行,并且比传统的命令行工具对程序员更友好。使用 sudo apt-get install ack-grep
在 Ubuntu 上安装它。
xargs
xargs 是一个旧的 Unix 命令行工具。它从标准输入读取项目并执行指定的命令,然后执行为标准输入读取的项目。所以基本上ack
生成的文件列表被附加到perl -pi -E 's/pattern/replacement/g'
命令的末尾。
perl -pi -E
Perl 是一种编程语言。
-p
选项使 Perl 围绕您的程序创建一个循环,该循环遍历文件名参数。
-i
选项使 Perl 编辑文件。您可以修改它以创建备份。
-E
选项使 Perl 执行指定为程序的一行代码。在我们的例子中,该程序只是一个 Perl 正则表达式替换。
有关 Perl 命令行选项的更多信息,请参阅perldoc perlrun
。有关 Perl 的更多信息,请参阅http://www.perl.org/。
【讨论】:
+1 由于没有安装 perl,我稍微使用了 sed 的解决方案:ack -l OLD_TEXT | xargs sed -i "" "s/OLD_TEXT/NEW_TEXT/g @Eric Johnson,你为什么在这里使用-E
切换perl
?我看不到任何功能的用处
@sputnick,我只是习惯性地使用 -E,所以我有“说”可用。 -e 当然也可以。
这会给我带来错误,除非我告诉 ack 只列出带有 -l
的文件,如下所示:ack -l 'pattern' | xargs perl -pi -E 's/pattern/replacement/g'
@tommychheng 在没有 perl 的情况下如何运行 ack?【参考方案2】:
现在,Vim 有了这个新命令 cdo
,它将对快速修复列表的每一行运行给定的命令。
所以你可以使用
:Ack pattern
:cdo s/pattern/newpattern/g
【讨论】:
这应该是公认的答案。效果很好,谢谢! 从 vim 7.4 开始,这是正确的答案。对于 OP,稍作调整, :cfdo 可能更合适。 我可能遗漏了一些明显的东西,但我在 Fedora 上运行 Vim 7.4,每当我尝试:cdo
时,我都会收到 not and editor command
,有什么想法吗?
从帮助页面中,您需要使用+listcmds
功能编译您的vim。它是否解释了缺少的命令?
:cdo s/pattern/newpattern/g | update
为我工作。【参考方案3】:
我不相信有一种内置的方法可以做到这一点,但应该很容易做到。
您需要做的是创建一个调用自定义函数的命令。然后该函数应该使用getqflist()
函数来获取快速修复列表中的所有条目,并使用exe
来完成脏活。小心你作为参数传递的内容!
" Define a command to make it easier to use
command! -nargs=+ QFDo call QFDo(<q-args>)
" Function that does the work
function! QFDo(command)
" Create a dictionary so that we can
" get the list of buffers rather than the
" list of lines in buffers (easy way
" to get unique entries)
let buffer_numbers =
" For each entry, use the buffer number as
" a dictionary key (won't get repeats)
for fixlist_entry in getqflist()
let buffer_numbers[fixlist_entry['bufnr']] = 1
endfor
" Make it into a list as it seems cleaner
let buffer_number_list = keys(buffer_numbers)
" For each buffer
for num in buffer_number_list
" Select the buffer
exe 'buffer' num
" Run the command that's passed as an argument
exe a:command
" Save if necessary
update
endfor
endfunction
【讨论】:
假设您执行:QFDo v/skipPat/s/Pat/Rep/gce
之类的操作,并且在 quickfix 窗口中有大量缓冲区。然后在第一个文件中,您注意到您输入了替换字符串 Rep。输入 Ctrl-c
不会停止命令,而只会停止当前行上的替换命令 - 对于每个缓冲区上匹配的每一行,都需要一个 Ctrl-c
。有什么技巧可以抓住Ctrl-c
或ESC
键并打破上面函数的for
语句?
根据上面的答案另见this article,它起源于this plugin。【参考方案4】:
你可以通过这种方式使用 ack
:args `ack -l User app/`
:argdo %s/, :expire.*)/)/ge | update
或者使用 ag
:args `ag -l User app/`
:argdo %s/, :expire.*)/)/gec | w
【讨论】:
【参考方案5】:我使用 MacVim(在 shell 中使用 mvim
激活)。我将ack
的结果通过管道传输到mvim
:
mvim -f $(ack -l $@)
然后在 MacVim 中,我使用 bufdo
搜索/替换:
:bufdo %s/SEARCH/REPLACE/gce | update
如果不需要确认,请省略c
选项。
【讨论】:
做 vim -p $(ack -Qil "string to match") 让这些在单独的标签中启动(如果这是你的事!)以上是关于如何在vim中用ack进行搜索和替换?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Vim 中的单个命令调用中从光标位置开始并环绕文件末尾进行全局搜索和替换?
如何使用正则表达式在 Intellij IDEA 中用小写替换大写?