2010-01-21 100 views
32

我发现了Vim的替代命令如何在Vim中的目录中递归搜索和替换?

:%s/replaceme/replacement/gi 

而且使用vimgrep ...

:vimgrep /findme/gj project/**/*.rb 

有没有办法给他们为了做所有的文件替换下结合目录?

+0

[Here](http:// stackoverflow。com/a/13944042/834176)是递归搜索和替换的一种方式,但仅限于版本控制下的文件(如果您确实想要搜索目录下的所有文件,可以很容易地进行推广)。 – 2012-12-19 01:06:51

回答

47

尽管我是Vim用户,但我通常使用findsed来处理这类事情。 sed的正则表达式语法与Vim(它们都是ed的后代)类似,尽管不完全相同。使用GNU sed,您还可以使用-i进行就地编辑(通常sed会将修改后的版本发送到stdout)。

例如:

find proj -name '*.rb' -type f -exec sed -i -e 's/regex/replacement/g' -- {} + 

一块一块:

  • 在 “凸出” 树proj =搜索
  • -name '*.rb' =东西的名字匹配 '* .RB'
  • -type f =并且是常规文件(不是目录,管道,符号链接等)。)
  • -exec sed =运行sed这些论点:
    • -i =与就地编辑
    • -e 's/regex/replacement/g' =执行 “替换” 命令(几乎相同的Vim的:s,但注意不足% - 它在sed默认)标志的
    • -- =结束,文件名从这里开始
    • {} =这就告诉find是TH它发现Ë文件名应放在sed中的命令行这里
    • + =这就告诉find-exec动作完成,我们要组参数成少数sed调用越好(一般比一次运行更高效每个文件名)。要为每个文件名运行一次,您可以使用\;而不是+

这与GNU sedfind一般的解决方案。在特殊情况下可以缩短一点。例如,如果你知道你的名字模式不匹配任何目录,你可以省略-type f。如果您知道所有文件都不以-开头,则可以省略--Here's an answer I posted to another question with more details on passing filenames found with find to other commands.

+6

确保并在给予'sed'之前测试你的'find' - 如果它过于包容,这可能非常糟糕。给'sed''-c'('--copy')选项也是一个好主意,这将防止它与只读文件混淆。 – Cascabel 2010-01-21 23:06:17

+0

@BroSlow够公平的。我已经更新了答案,以便更安全/更一般。 (也就是说,在Linux上,至少ARG_MAX是相当大的,对于问题中描述的用例,使用空格或特殊字符的文件名会非常奇怪。) – 2014-11-10 00:49:47

+0

如果使用此方法,则不能使用该构造,因为sed不支持它,你可以使用'vim -c'%/ regex/replacement/gc'-c'wq' - {} \;'这样你就可以确认你的改变了 – Funonly 2015-04-23 09:22:58

4

有一个插件正在努力做到这一点:Vimgrep Replace

哦,等等......还有其他的:Global Replace,EasyGrep

对于一个没有插件的解决方案,或许argdo将有助于如果你能得到vimgrep的输出到参数列表(可以用args设置),但我似乎无法弄清楚的细节。如果有人采纳了这个想法并对其进行改进,我会很高兴......所以在这里。

第一个插件(我猜别人也是......)的基本思想是首先使用vimgrep,然后在:cnext之间循环匹配,并在每行上应用替换命令。完成此功能的函数可能足够小,可以放入.vimrc中。 (也许你可以举一个插件的来源?)

(我想hgimenez可能是一个解决方案,但不管它是否合适可能取决于要处理的文件数量......插件应该没问题。)

+0

谢谢,但arrrrgh!有没有办法做到这一点,而无需安装另一个#!*&%插件? – Ethan 2010-01-21 21:48:33

+0

@Ethan:我感觉你。 :-)我认为必须有一种方法涉及:args,:argdo,:copen或其他......我只是不使用vimgrep足够以前对它有过多的考虑,似乎无法将它们放在一起对你来说完美的解决方案。但我仍在尝试,所以也许我会想出一些东西并进行编辑。 – 2010-01-21 21:52:53

+0

噢,好吧......我似乎无法找到一个干净简单的无插件解决方案,或者确定地告诉我自己没有任何插件。我的Vim-fu让我失望了! :-(插件的确看起来不错,但也许它们值得一试...... – 2010-01-21 22:32:30

4

一种方法是打开所有要运行替换(或任何命令)的文件,然后运行:bufdo <cmd>,它将在所有打开的缓冲区上运行<cmd>

7

您可以运行使用vimgrep然后录制去在使用vimgrep输出每一行,并没有替换,像这样一个宏:(!不要键入#评论)

:set autowrite     #automatically save the file when we change buffers 
:vimgrep /pattern/ ./**/files 
qa        #start recording macro in register a 
:s/pattern/replace/g   #replace on the current line 
:cnext       #go to next matching line 
q        #stop recording 
[email protected]       #repeat the macro 10000 times, or until stopped by 
           #an "error" such as reaching the end of the list 
:set noautowrite 

我没有测试过它,所以它可能需要一点调整 - 在一些你不介意首先搞砸的文件上测试它。

这个优于sed的优点是Vim正则表达式更加强大,您可以添加c选项来:验证每个替换项。

编辑: 我修改了原来的帖子,因为它是错误的。我正在使用:1vimgrep来查找每个文件中的第一个匹配项,但我误读了文档 - 它仅在所有文件中找到一个匹配项。

+0

很酷!在寻找单一的表达解决方案时,我从来没有停下来考虑一个宏观...让我现在感觉有点傻。 +1。 :-) – 2010-01-23 14:36:58

+0

你刚刚救了我几个小时的工作,谢谢! :D我需要将xml文件中的值复制到文件的另一部分,然后小写 - 超过200个文件。 – Jason 2012-05-04 15:32:30

45

args and argdo应该做你需要的,例如,

:args spec/javascripts/**/*.* 
:argdo %s/foo/bar/g 

请参阅this page

+0

谢谢!这是非常酷:) – starikovs 2015-04-20 06:03:58

+1

Whoa vimskill ++ – 2017-02-02 18:17:31

+1

年的苦难和质疑职业选择今天结束 – 2017-04-27 05:26:11

0

这适用于少量文件。

vim`find。 -type f`

:bufdo%s/pattern/replacement/gec |更新