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

1hdlvixo  于 2023-06-23  发布在  其他
关注(0)|答案(8)|浏览(172)

我发现了 vim 的替代命令

:%s/replaceme/replacement/gi

还有vimgrep..

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

有没有一种方法可以将它们组合在一起,以便在一个目录下的所有文件中进行替换?

xxls0lw8

xxls0lw81#

argsargdo应该做你需要的事情,例如

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

请参阅此页。

enxuqcxy

enxuqcxy2#

尽管我是一个Vim用户,但我通常使用findsed来处理这类事情。sed的正则表达式语法与Vim的语法类似(它们都是ed的后代),但并不完全相同。使用GNU sed,您还可以使用-i进行就地编辑(通常sed将修改后的版本发送到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的命令行中
  • + = this告诉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.

bybem2ql

bybem2ql3#

您可以运行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

我还没有测试它,所以它可能需要一点调整-测试它的一些文件,你不介意得到混乱的第一。
与sed相比,Vim正则表达式的优点是更强大,您可以将c选项添加到:s以验证每个替换。

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

jgovgodb

jgovgodb4#

要回答将vimgrep和substitute组合在一起的原始问题:

: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

如果你不想确认每个替换,可以省略gicc(在这种情况下,你也可以按照@LaurenceGonsalves的建议使用findsed)。

yrdbyhpb

yrdbyhpb5#

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

s8vozzvw

s8vozzvw6#

有一个插件,努力做到这一点:Vimgrep Replace
等等,还有其他人:Global ReplaceEasyGrep
对于一个没有插件的解决方案,如果你能把vimgrep的输出放到参数列表上(可以用args设置),也许argdo会有所帮助,但我似乎不能弄清楚细节。如果有人接受这个想法并加以改进我会很高兴...所以就这样了
第一个插件背后的基本思想(我猜其他插件也是如此......)是首先使用vimgrep,然后循环使用:cnext进行匹配,并在每行应用替换命令。完成此任务的函数可以足够小,可以将其放入. vimrc中。(也许你可以从插件的源代码中提取一个?)
(我想hgimenez可能会找到一个解决方案,但它是否合适可能取决于要处理的文件数量。不管怎样,插件应该是好的。)

xj3cbfub

xj3cbfub7#

这适用于少量文件。
vim find . - 类型f
:bufdo %s/pattern/replacement/gec|更新

ctehm74n

ctehm74n8#

下面是我的Neovim用户命令,它可以使用grep或grep并替换。它将:vimgrep并在quickfix列表窗口中显示结果,因此您可以导航和查看已进行的更改。您可以使用:wall应用所有更改:

-- Grep
vim.api.nvim_create_user_command(
'Grep',
function(opts)
    local usage = "Usage: :Grep <regex> <filter?>, i.e. :Grep /my_string/g **/*, or :Grep! s/my_string/my_replacement/g **/*"
    if not opts.args or string.len(opts.args) < 1 then
        print(usage)
        return
    end
    local delim = string.sub(opts.args, 1, 1)
    if delim == "s" then
        delim = string.sub(opts.args, 2, 2)
    end
    local pargs = vim.split(opts.args, delim)
    if #pargs < 3 then
        print(usage)
        return
    end
    local searchpatt = pargs[2]
    if pargs[1] == "s" and #pargs < 4 then
        print(usage)
        return
    end
    local replpatt = nil
    local flags = nil
    local filter = ""
    if pargs[1] == "s" then
        if not opts.bang then
            print(usage)
            return
        end
        replpatt = pargs[3]
        local bridge = vim.split(pargs[4], " ")
        flags = bridge[1]
        filter = bridge[2] or ""
        for i = 5, #pargs do
            filter = filter .. delim.. pargs[i]
        end
    else
        if opts.bang then
            print(usage)
            return
        end
        local bridge = vim.split(pargs[3], " ")
        flags = bridge[1]
        filter = bridge[2] or ""
        for i = 4, #pargs do
            filter = filter .. delim .. pargs[i]
        end
    end
    -- print("d:" .. delim .. "\ns:" .. searchpatt .. "\nr:" .. (replpatt or "nil") .. "\nm:" .. flags .. "\nf:" .. filter)
    if searchpatt and flags and filter then
        vim.cmd(":vimgrep /" .. searchpatt .. "/" .. flags .. "j " .. filter)
        vim.cmd(":cw")
        vim.cmd(":.cc")
        if replpatt then
            vim.cmd(":cfdo %s/" .. searchpatt .. "/" .. replpatt .. "/" .. flags .. "e")
        end
    end
end,
{ nargs = '*', bang = true }
)

在正则表达式中不应转义空格。g标志是强制性的,s标志与bang(!)组合是强制性的,bang(!)将引入破坏性替换操作。
您可以使用:cn/:cp浏览quickfix列表,也可以使用:ccl关闭它。
这篇文章帮助我建立了这个:https://jdhao.github.io/2020/03/14/nvim_search_replace_multiple_file/

相关问题