Vim搜索和替换选定文本

149

假设我们有一段文本,我进入了视觉模式并选择了一些文本。如何快速搜索高亮显示的文本并将其替换为其他内容?


1
这个问题展示了vim最令人沮丧的缺陷之一。在任何图形编辑器中,这项工作可以在几秒钟内完成。但正如所有提供的答案所显示的那样,在我们让vim执行相同操作之前,我们必须跳过许多障碍。 - user9224371
15个回答

175
尝试执行以下命令,或将其放入您的 .vimrc 文件中。
vnoremap <C-r> "hy:%s/<C-r>h//gc<left><left><left>

在可视模式下,按下ctrl+r,即可提示输入要替换的文本。按下enter,然后确认是否接受更改,按下 y接受或按下n拒绝。

此命令将覆盖您的寄存器h,因此您可以选择其他未使用的寄存器(通过将上述命令中的h更改为另一个小写字母)。


18
如果您在 gc 中省略了 c,则会立即在整个缓冲区中进行替换。如果您犯了一个错误并想撤销它,请在命令模式下键入 u。我喜欢这种方法比逐个确认替换更好(使用 c 修饰符会发生这种情况)。 - Niloct
3
vmap * y:let @/ = @"<CR> 的意思是选中文本后,执行该命令可以将选中的内容复制到寄存器中,并将其作为搜索模式。 - kev
10
"<left><left><left>" 的目标是什么? - aymericbeaumet
7
将光标放置在正斜杠之间,这样替换单词就可以轻松输入。 - mcabrams
6
请注意,如果您采纳了@Niloct有关从gc中删除c的建议,则还必须从该行末尾删除一个<left> - Nathan Friend
显示剩余9条评论

48

这个快速简便的映射搜索适用于在视觉上突出显示文本(不覆盖 h 寄存器)且不使用终端相关的 + 寄存器:

" search for visually hightlighted text
vnoremap <c-f> y<ESC>/<c-r>"<CR>   

如果你不喜欢Ctrl-F,请将其更改为你喜欢的方式。

一旦文本被突出显示,你可以通过键入以下内容来进行替换:substitution。

%s//<your-replacement-string>

...因为在s命令的空白搜索中使用上一次搜索的字符串。


当我在我的vim配置中使用这个时,control+f总是选择下一个字符以及我可视选择的内容。有什么想法吗? - Robert Mark Bram
%s//<your-replacement-string> doesn't seem to work for highlighting something like /tmp/file. it just becomes <your-replacement-string>/tmp/file - naisanza

47

这种方法也适用(至少对于单行选择/不包含任何特殊字符的选择)

  • 在可视模式下选择文本
  • 使用y复制文本
  • :s/<C-r>0/

0是复制寄存器。

正如其他人所提到的,有更快的方法可以完成此操作,但如果您刚开始学习Vim(就像我一样),则这是“通用”方法之一。

//编辑:我刚刚意识到“寄存器”方法将无法处理选择中存在制表符或换行符或/字符的情况(我必须手动处理这些字符以便:s命令能够工作):

进入图像描述


3
非常简单。哦天啊,为什么维基上没有涉及到这个。拜托了...谢谢你的回答! - Benyamin Limanto
6
对于我们这些仍处于 Vim 初学阶段的人来说,这是理想的答案。我知道自定义是一个优点,但对于这样简单的问题,我不想将复杂的脚本复制粘贴到我的 vimrc 文件中,然后费劲地搞清楚它是如何组合在一起的。这个答案教会了我 ctrl-R 如何转储寄存器,在其他情况下记住这个技巧也是很有用的。 - nivek
2
关于您最后一次编辑有关特殊字符(路径中有斜杠/)的内容,您可以简单地使用其他分隔符(例如或|,选择适合您的)。这样,我用星号作为分隔符替换了整个文件中的复杂路径,并使用类似于:%s*<C-r>0*/Users/very/long/path/here/*gc的命令。 - bosch

34

如果您的可视选择中含有特殊字符,则接受的答案效果很好。我已经组合了两个脚本(Jeremy Cantrell在这里发布的, Peter Odding的),以创建一个命令,即使要查找的字符串中有特殊正则表达式字符,也可以进行可视选择。

" Escape special characters in a string for exact matching.
" This is useful to copying strings from the file to the search tool
" Based on this - http://peterodding.com/code/vim/profile/autoload/xolox/escape.vim
function! EscapeString (string)
  let string=a:string
  " Escape regex characters
  let string = escape(string, '^$.*\/~[]')
  " Escape the line endings
  let string = substitute(string, '\n', '\\n', 'g')
  return string
endfunction

" Get the current visual block for search and replaces
" This function passed the visual block through a string escape function
" Based on this - https://dev59.com/v3RB5IYBdhLWcg3wQVSV#677918
function! GetVisual() range
  " Save the current register and clipboard
  let reg_save = getreg('"')
  let regtype_save = getregtype('"')
  let cb_save = &clipboard
  set clipboard&

  " Put the current visual selection in the " register
  normal! ""gvy
  let selection = getreg('"')

  " Put the saved registers and clipboards back
  call setreg('"', reg_save, regtype_save)
  let &clipboard = cb_save

  "Escape any special characters in the selection
  let escaped_selection = EscapeString(selection)

  return escaped_selection
endfunction

" Start the find and replace command across the entire file
vmap <leader>z <Esc>:%s/<c-r>=GetVisual()<cr>/

如果对任何人更有用,我已经将这个东西包含在我的vimrc中。


5
感谢你的解决方案,结合多个建议后,我最终得到了以下内容:vmap <C-r> <Esc>:%s/<c-r>=GetVisual()<cr>//g<left><left>(用于替换整个文件中符合条件的文本)。 - Peter Butkovic
这里需要递归的 vmap 吗?无论如何,谢谢,这是一个宝石,看起来可以解决我的问题 https://vi.stackexchange.com/questions/41421/how-to-grab-visual-selection-for-search-but-apply-the-appropriate-escapes 另外,使用 ctrl+R 是聪明的。我没有考虑到视觉模式与正常模式在不同的按键绑定空间中。 - Steven Lu

28
自 Vim 7.4 起,你可以使用 gn 命令选择与当前搜索模式匹配的文本区域。
在选定匹配项后使用 cgn 命令更改文本(或使用 dgn 命令删除),在正常模式下,您可以使用 . 命令重复操作并作用于下一个匹配项。
Vimcast 中有一集介绍了这个功能:操作搜索匹配项使用 gn 命令

4
这是一个很棒的答案! - mauriciojost
这也很棒。除了ColeMik的答案之外,非常好! - Benyamin Limanto
哇,这就像是当你深入浏览以找到一个好的解决方案时发现的那些秘密一样,谢谢! - vdegenne
五年后,我补充说这个插件很好,因为它可以在不需要更改 .vimrc 的情况下使用,所以在服务器上的会话中更加适用,这是 vim 的共同点。非常好。 - Gustavo Pinsard
很棒的答案,但是在 NeoVim v0.8.2 中,如果你用 gn 扩展可视化选择,然后按下 cgn,它将立即跳转到插入模式,而不需要等待 gn。按下 n 然后 . 将此操作应用于以下匹配项。 - dogeen

13

摘自http://vim.wikia.com/wiki/Search_and_replace_in_a_visual_selection

当文本被可视化选择时,按下 : 键进入命令模式。命令行将自动输入所选范围:

:'<,'>

您可以输入类似 s/red/green/g 的命令,以将最后一个可视选择中所有行中的每个 red 替换为 green。该命令将显示为:

:'<,'>s/red/green/g

要在之前选择的块上重复执行Ex命令,请使用 : 历史记录。也就是说,按下 : 键,然后按键,接着编辑一个以前的命令。


10
是的,这将在所选区域内用绿色替换红色 - 非常有用,但不是原作者要求的内容。 - ErichBSchulz
@ErichBSchulz 实际上它会在所有选定的行上执行。因此,即使只选择了一行的一部分,它也会对整行执行操作。 - Cully
2
也许这并不回答楼主的问题,但它确实回答了我的搜索需求:“使用视觉选择进行vim搜索和替换”,我想限制搜索和替换仅在视觉选择范围内进行,从而引出了这个问题。 - icc97

9

Mykola Golubyev,感谢您的提示!以下使用“+”寄存器,该寄存器(根据您的终端)已包含高亮文本,因此无需使用“h”寄存器。

vnoremap <C-r> <Esc>:%s/<C-r>+//gc<left><left><left>

我的CentOS 5.x上的VIM 7.0似乎没有+寄存器。这是一个标准功能吗? - dotancohen
4
我注意到我通过Putty访问CentOS机器,并通过搜索发现+是剪贴板寄存器。然而,看起来Putty不支持该功能!因此,这个答案实际上与终端有关。 - dotancohen
嗨,我尝试了你的命令,但它要求我替换整个文件中指定文本的所有出现,而不仅仅是所选文本。 - solalito

6
我希望Vim中有一种内置的方式可以替换一个单词,而不需要重新输入该单词。实际上是有的,但它仅适用于长度在单词界限字符以内的文本。
当你将鼠标悬停在单词上时,按下“*”或“#”来突出显示该单词并跳转到它的下一个/上一个位置(如果您不想在文件中更改位置,则按相反的键返回到刚才所在的位置)。
然后只需键入:
%s// 如另一个人的答案中所述,如果在斜杠之间输入了空白条目,则%s使用先前搜索的单词。通过按“*”或“#”,您正在搜索光标下的单词,这使它成为最近搜索的单词。
最后,只需输入六七个按键加上替换单词的长度即可完成此操作,无需宏或编辑您的.vimrc文件。

5

我在我的vimrc文件中有以下内容:

function! GetVisual() range
    let reg_save = getreg('"')
    let regtype_save = getregtype('"')
    let cb_save = &clipboard
    set clipboard&
    normal! ""gvy
    let selection = getreg('"')
    call setreg('"', reg_save, regtype_save)
    let &clipboard = cb_save
    return selection
endfunction

vmap <leader>z :%s/<c-r>=GetVisual()<cr>/

这将获取视觉选择并使用它开始替换命令。

编辑:需要指出的是,这不能处理多行视觉选择。虽然GetVisual()没有问题返回它,但我不确定如何正确地将其放入命令行中。如果有人有关于我如何做到这一点的提示,请留言评论。


4
其他答案都很好,但它们没有转义特殊字符。@brian kennedy的答案展示了一种转义方法,但需要较多的代码。例如在URL中,转义是必须的,否则搜索将停在“:”上。
正如vim.fandom.com/wiki/Search_for_visually_selected_text中所写的那样,您可以使用一行命令来搜索和转义某些字符。您可以转义您想要的字符,我选择转义/\:
vnoremap // y/\V<C-R>=escape(@",'/\:')<CR><CR>

使用这张地图,我必须在可视化模式下按 // 来搜索整个缓冲区中当前选定的文本。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接