如何在Vim中的目录中递归搜索和替换?

Eth*_*han 40 vim recursion search

我发现了Vim的替代命令......

:%s/replaceme/replacement/gi
Run Code Online (Sandbox Code Playgroud)

和vimgrep ......

:vimgrep  /findme/gj  project/**/*.rb
Run Code Online (Sandbox Code Playgroud)

有没有办法将它们组合起来,以便在目录下的所有文件中进行替换?

Lau*_*ves 65

虽然我是一个Vim的用户,我通常使用find,并sed为这样的事情.正则表达式的语法sed类似于Vim(它们都是后代ed),尽管不完全相同.使用GNU sed,您还可以使用-i就地编辑(通常sed将修改后的版本发送到stdout).

例如:

find project -name '*.rb' -type f -exec sed -i -e 's/regex/replacement/g' -- {} +
Run Code Online (Sandbox Code Playgroud)

一块一块:

  • project =在"项目"树中搜索
  • -name '*.rb' =对于名称与'*.rb'匹配的内容
  • -type f =和常规文件(不是目录,管道,符号链接等)
  • -exec sed= sed使用以下参数运行:
    • -i =使用就地编辑
    • -e 's/regex/replacement/g'=执行"替换"命令(这几乎与Vim相同:s,但注意缺少%- 这是默认值sed)
    • -- =标志结束,文件名从这里开始
    • {}=这告诉find它找到的文件名应该放在sed的命令行上
    • +=这find表示-exec操作已完成,我们希望将参数分组为尽可能少的sed调用(通常比每个文件名运行一次更有效).要为每个文件名运行一次,您可以使用\;而不是+.

这是GNU sedfind.的一般解决方案.在特殊情况下,这可以缩短一点.例如,如果您知道您的名称模式与您可以省略的任何目录不匹配-type f.如果您知道没有任何文件以a开头,则-可以省略--.这是我发布到另一个问题的答案,其中包含有关将文件名传递find给其他命令的更多详细信息.

  • 在给它'sed`之前确保并测试你的'find` - 如果它过于包容,这可能真的很糟糕.给`sed`提供`-c`(`--copy`)选项也是一个好主意,这样可以避免乱用只读文件. (8认同)
  • 如果你使用这种方法,你不能使用构造,因为 sed 不支持它,你可以使用 `vim -c '%/regex/replacement/gc' -c 'wq' -- {} \;` 这样你也可以确认您的更改 (2认同)

mel*_*yal 58

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

:args spec/javascripts/**/*.* 
:argdo %s/foo/bar/g
Run Code Online (Sandbox Code Playgroud)

看到这个页面.

  • 多年的痛苦和质疑职业选择今天结束了 (6认同)
  • 哇vimskill ++ (3认同)
  • 加静音!argdo%s / foo / bar / g | 更新`,以忽略那些不匹配的内容,这对我来说要好得多。 (2认同)

Dav*_*rby 7

您可以运行vimgrep然后记录一个宏,该宏转到vimgrep输出中的每一行并执行替换,如下所示:(不要输入#comments!)

: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
10000@a                          #repeat the macro 10000 times, or until stopped by 
                                #an "error" such as reaching the end of the list
:set noautowrite
Run Code Online (Sandbox Code Playgroud)

我还没有测试过,所以可能需要稍微调整一下 - 在一些你不介意先搞砸的文件上测试它.

这个优于sed的优势在于Vim正则表达式更强大,您可以将c选项添加到:s以验证每次替换.

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


小智 7

回答结合 vimgrep 和替换的原始问题:

:vimgrep /pattern/ ./**/files              # results go to quickfix list
:set autowrite                             # auto-save when changing buffers
:silent! cdo %s/replaceme/replacement/gic  # cdo draws filenames from quickfix list
:set noautowrite
Run Code Online (Sandbox Code Playgroud)

离开了cgic,如果你不想确认每个替换(在这种情况下,你也可以使用find,并sed通过@LaurenceGonsalves的建议)。


hgm*_*mnz 5

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