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

问题描述:

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

I found out about Vim's substitute command...

:%s/replaceme/replacement/gi

还有 vimgrep...

And vimgrep...

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

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

Is there a way to combine them in order to do a replacement across all the files under a directory?

尽管我是 Vim 用户,但我通常使用 findsed事物.sed 的正则表达式语法类似于 Vim(它们都是 ed 的后代),但并不完全相同.使用 GNU sed,您还可以使用 -i 进行就地编辑(通常 sed 将修改后的版本发送到标准输出).

Even though I'm a Vim user, I generally use find and sed for this sort of thing. The regex syntax for sed is similar to Vim's (they're both descendants of ed), though not identical. With GNU sed you can also use -i for in-place edits (normally sed emits the modified version to stdout).

例如:

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

逐个:

  • project = 在项目"树中搜索
  • -name '*.rb' = 用于名称匹配 '*.rb' 的事物
  • -type f = 并且是常规文件(不是目录、管道、符号链接等)
  • -exec sed = 使用这些参数运行 sed:
    • -i = 就地编辑
    • -e 's/regex/replacement/g' = 执行替换"命令(这几乎与 Vim 的 :s 相同,但请注意缺少 % -- 这是sed中的默认值)
    • -- = 标志结束,文件名从这里开始
    • {} = 这告诉 find 它找到的文件名应该放在 sed 的命令行中
    • + = 这告诉 find -exec 操作已完成,我们希望将参数分组为尽可能少的 sed 调用(通常比每个文件名运行一次更有效).要按文件名运行一次,您可以使用 \; 而不是 +.
    • project = search in "project" tree
    • -name '*.rb' = for things whose names match '*.rb'
    • -type f = and are regular files (not directories, pipes, symlinks, etc.)
    • -exec sed = run sed with these arguments:
      • -i = with in-place edits
      • -e 's/regex/replacement/g' = execute a "substitute" command (which is almost identical to Vim's :s, but note lack of % -- it's the default in sed)
      • -- = end of flags, filenames start here
      • {} = this tells find that the filenames it found should be placed on sed's command-line here
      • + = this tells find that the -exec action is finished, and we want to group the arguments into as few sed invocations as possible (generally more efficient than running it once per filename). To run it once per filename you can use \; instead of +.

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

      This is the general solution with GNU sed and find. This can be shortened a bit in special cases. For example, if you know that your name pattern will not match any directories you can leave out -type f. If you know that none of your files start with a - you can leave out --. Here's an answer I posted to another question with more details on passing filenames found with find to other commands.