想必用过Vim的人都知道,在Vim里面,以下命令可以替换当前文件的内容:
:[range]s/{要被替换的模式}/{替换的内容}/[flags]
其中range
指定替换命令生效的范围。flags指定替换的一些选项,常用的有:
c 替换前进行确认
g 如果缺乏该选项,只会替换第一个。一般我们所说的替换是全部替换,即加了g选项的替换。
i 忽略大小写
e 忽略错误
具体细节请:vert help substitude
查看。
本文将以此为起点,介绍一些替换小技巧。写这篇文章,主要是分享个人的一些脑洞心得,顺便向接触过Vim的人安利下Vim的一些哲学。
实话实说,使用命令来替换有违于(大多数)程序员的习惯。一般提起“替换”,第一感觉都是按下某个快捷键,然后在某个窗口中输入“查找内容”,再输入“替换内容”,按确定。靠敲命令来替换,总会让人想起sed
这样的老东西,想起它那咒文一样的指令(如果还想得起来的话)。
还好,Vim的映射机制让想起这一切不再困难。你仅需映射一份模板:
" 把下面映射添加到vimrc中
" 设置替换命令的模板
nnoremap <leader>s :%s///gc<left><left><left><left>
" :vert help c_<C-R>
" Ctrl-r " 插入最近一次复制/删除的文本
nnoremap <leader>sl :%s/<C-R>"/<C-R>"/gc<left><left><left>
" Ctrl-r Ctrl-w 插入当前光标下的词。
nnoremap <leader>sc :%s/\<<C-R><C-W>\>/<C-R><C-W>/gc<left><left><left>
这样就不用记住完整的替换命令了,仅需填两个空。是不是又回到了熟悉的“查找内容”/“替换内容”模式呢?
Vim哲学第N条:用映射消除重复的劳动。
一般在文本编辑的过程中,常常会有重复某几个步骤的情况。一个合格的Vimer应该学会用映射或其他机制来减少无益的操作。所谓时间就是生命,珍爱生命,从灵活使用映射开始。
" :vert help map 查看如何在Vim中使用映射
" :vert help recording 查看如何在Vim中使用录制
看了刚才的内容,应该不会对Vim里面的替换操作感到陌生了。接下更进一步,教多几个小技巧。
回顾替换命令:
:[range]s/{要被替换的模式}/{替换的内容}/[flags]
最前面的range
也是有些门道的。range
是Vim中的一个概念,表示文本的某个范围。常用的range
有两种:一种是m,n
,表示从第m行到第n行,其中.
表示当前行,而$
表示最未行;另一种是%
,表示整个文件,等价于1,$
。更多的形式请查看:vert help range
。
凭借这一点,我们可以实现指定替换的范围,减轻确认时的工作量。下面介绍个例子:
vnoremap <leader>s :s///gc<left><left><left><left>
这个跟前面的normal模式下的设置模板的映射很像,不过有两点不同。一点是,这是作用在visual模式下的映射;另一点是,这个模板里没有指定范围。在visual模式下使用命令,默认范围是当前选中的范围。(参见:vert help v_:
)
于是乎,我们可以这样使用:
敲
vi{
选中当前大括号(代码块)里面的内容。敲
<leader>s
发动映射。
这么一来,替换将仅在当前大括号内生效。在替换局部变量时,比起全局替换,这样的替换方式无疑会更高效。
什么?你说你用Python?嗯,你可以考虑下借助第三方插件来选中代码块:
Vim哲学第N+1条:用好组合技
很多情况下,替换操作涉及多个文件。由于缺乏项目管理的功能,编辑器在这方面自然比不上IDE。不过Vim还是支持对多个文件执行替换操作,虽说有点儿粗糙。Vim提供了名为argdo
的机制,可以在多个文件上执行同样的命令。
" 在dataType.cpp和dataType.h中替换filename为fn
:args dataType.cpp dataType.h
" :vert help argdo
:argdo %s/filename/fn/gce | update
args
命令接收文件列表,而argdo
命令接收要执行命令,update
则写入更新了的文件内容。(注意这里的|
是用来连接%s/filename/fn/gc
和update
成单一的参数,不是管道符)
关于args
的更多内容,参见这篇文章:http://vimcasts.org/episodes/populating-the-arglist/
我们可以更进一步,实现全项目内的替换。假设你的项目用git作版本管理,那么通过git ls-files
可以获取全部文件名。然后用grep -l pattern $(git ls-files)
可以筛选出含有pattern
的文件。接下来就是把这份文件列表传递给args
:
" 查找整个项目中含有filename的文件,并作为参数传递给args
:args `grep -l filename $(git ls-files)`
:argdo %s/filename/fn/gce | update
就是这样。
Vim哲学第N+2条:善用外部命令来拓展Vim的能力
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。