Vim实用技巧3

背景

今天偶然看到本书中一个灰常牛皮的技巧,光看其名称就能知晓该玩意儿异乎寻常:宏。宏乃vim自动化的巅峰之作,能够将原本重复机械的操作录制下来,作用于其他符合条件的地方。除此之外,今天对查找命令/和替换命令:s/{string}/{string}/{option}做一些记录

内容

宏命令

宏命令让我们可以拥有属于自己的自动化员工。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 录制宏命令的标志是q, 后面跟随的是对应的寄存器
q{register}

# 停止录制
q

# 如:qa --> 录制宏并保存进a寄存器中

# 调用指定寄存器中的宏
@{register}

# 如:@a --> 调用a寄存器中的宏命令

# 调用最近一次执行的宏命令
@@

当我们需要这个员工反复工作100的时候。

1
2
3
4
# 执行指定数目的任务
[num]@{register}

# 如:100@a --> 执行100次a寄存器中的宏

又或是我们需要这个员工在指定的范围内才工作,这是就需要使用normal指令了。

1
2
3
4
# 在指定的范围内工作
:[range]normal @{register}

# :100,200normal @a --> 在第100行和200之间执行a寄存器的宏

说到此处,则需要警戒的一点就是:宏命令在执行的过程中如果发生错误,则会停止执行之后的命令

But,有时候执行的时候才发现宏命令没有录制完成,很幸运的是,vim为我们提供了补录的方式

1
2
# 和寄存器一样,使用小写则冲掉原寄存器的内容,使用大写则可以将内容追加到寄存器的内容之后
qA --> 将接下来的操作追加到a寄存器的尾部

虽然已经又了可以补录的操作,可如果我们需要编辑我们刚刚录制的宏内容呢?此时就需要结合多条命令进行操作了

1
2
3
4
5
6
7
8
9
10
# 将对应寄存器中的内容打印到当前vim作业中
:put {register}

# 然后选中命令的内容,利用x/d将编辑修改后将内容重新回写至对应的寄存器中
"{register}d$

# 由于直接删除会将换行符也录入寄存器,所以使用$表示命令的尾部

# 然后再执行该寄存器即可
@{register}

上面查看宏内容的方式是将其打印在当前的vim作业中,本质上会修改作业的上下文,着实不便,那么当我们仅仅需要查看宏内容的时候,应该怎么办呢?其实查看宏命令就是查看寄存器的内容,因此可以直接访问寄存器的内容即可。

1
2
# 查看寄存器的内容
:reg {register}

内容查找

此前我们说到查找命令/时只论述了普通的查找方式,并未深入。但/本身是支持正则的,不过如果直接在其后面编辑正则表达式,则需要对(){}<>进行转义(加\)才可以,并不直观,为此,vim提供了指令\v、\V,用于直接编写正则表达式

1
2
3
4
5
# \v表示进行正则匹配,后面跟的内容是正则匹配的内容
/\vX(ht|m)l --> 表示匹配Xhtml/Xml单词的内容

# \V表示原义匹配,这个是因为/后面的.?*等符号都是通配符,如果需要准确匹配也需要手动转义,为了不用手动转义而引入
/\Vshuai.shuai

说到此处,则需要提一点,/指令能够直接识别的只有:#、_、数字、字母

/{string}查找时默认是区分大小写的,而有时,我们查找的时候是不分大小写的查找。为此,vim提供了指令:\c、\C

1
2
3
4
5
# \c表示不区分大小写
/\c{string}

# \C表示区分大小写,这个也就是默认的,有时加上是为了防止默认设置被篡改
/\C{string}

那么是否可以将\v、\c结合呢?答案当然是可以

1
/\v\c{string}  --> 它会不区分大小写匹配正则表达式的内容

说起正则,天下的正则大同小异,vim的正则与python(我只知道一点点的)中的类似,只是除了通用的正则元字符外,文中还提供了其它的元字符,以供参考,但是我想说的是,如果需要对这些元字符进行准确匹配,则需要自己手动转义

1
2
3
4
5
# <表示匹配单词的开始,>表示匹配单词的结束,对应于正则中的\b
/\v<shuai> --> 匹配单个单词shuai,对于包含shuai的单词则不进行匹配

# \_s表示匹配空白符或换行符,对应正则中的\s
/\v<shuai>\_s<piao> --> 匹配空格链接的shuai piao,或者是换行符链接的shuai piao

其实上述的内容,只仅限于查找,还不是/命令的神奇之处,最让我惊愕的是/能够将匹配的内容直接存入对应的寄存器中,让其它指令进行调用

1
2
3
4
5
6
7
# 从左向右,用括号依次包裹的内容会被依次存入\1    ~ \9的寄存器中
/\v(shuai)(piao) --> 将shuai存入了\1寄存器中,piao存入了\2寄存器中

# 然后我们通过访问\1~\9寄存器,将其中的内容取出,
:%s//\2-\1/g --> 在匹配到字符以后,我们将原本1位置的字符与2位置的字符进行了互换并回写vim作业中

# 替换命令中\1、\2只作用于匹配的对象之中。

而有时,我们需要取某个环境下的某一小块的内容,针对这种情况,我们需要拿大环境做正则匹配,然后再取内部我们需要的内容。vim提供了这样的指令:\zs、\ze\zs匹配裁剪的起始位置,\ze匹配裁剪内容的终止位置,如:

1
2
3
/\v"\zsshuai\ze"  —> 表示只选取双引号包裹的shuai

# 其中\zs、\ze都是可以单独使用的

上述方式,可以让我们更加灵活的查找内容,但是对于我们编辑查找命令和避免错误时却不是那么的友好。vim本身提供了实时预览的增强功能,对应的插件时:incsearch,默认不开启,需要手动开启:

1
2
3
4
5
6
7
8
# 这种方式,随着我们编辑的内容,会实时的进行匹配并切换画面到
# 如果决定不查找了,直接 [ESC]则可以回到查找前的光标下
:set incsearch

# 如果在编辑模式下,想将光标指定的内容直接智能复制到输入框,则
C-r C-w

# 但是C-r C-w在\v正则模式下无法智能复制,所以不建议在编写正则查找时使用

前面描述的都是精确查找,但有时我们也会遇到需要偏移的情况,vim也支持偏移查找的方式

1
2
# 通过在匹配内容后面追加/e可以直接跳转到查询单词的尾部,实测后发现其它跳转指令无动于衷
/{string}/e

此前我们都是通过/引导出的输入框按上下键切换历史命令进行编辑,但vim还提供了历史命令窗口

1
2
3
4
5
# 查看历史查找命令
q/

# 退出历史查找命令
:q

一般情况下,我们都是直接通过n、N来重复最近一次的查找命令,但实际上,vim还提供了另一个方式

1
2
# 执行最近一次的/查找命令
//

这个//的作用倒不是体现在重复查找上,而是体现在访问最近一次的查找内容上。当其与x、s、d、c进行搭配时,就会展示惊人的力量,实测它会将匹配内容所在的那一句都操作掉,原因不明。

1
2
3
4
d//  --> 删除一次匹配/的所在句子的内容.

# 但是替换命令就很准确了
:%s//shuai/g. --> 将所有准确匹配的地方全部用shuai替换掉

替换命令

针对替换命令就是:s/{pattern}/{replace}/{option},pattern本身支持正则,

1
2
# pattern中匹配括号的内容同时会写入\1~\9的寄存器中,在string中可直接使用,如
:%s/(\w+),(\w+),(\w+)/\3,\1,\2/g --> 将单词的位置调换顺序

option则有三个选项:

1
2
3
4
5
6
7
8
# option为g表示作用域内匹配的全替换
:%s/shuai/qi/g

# option为n表示不替换,只是显示符合规则的匹配项的个数
:%s/shuai/q/n

# option为c表示替换前需要人工确认
:%s/shuai/qi/gc

同样在编辑替换命令的时候,如果需要将最近一次查找的内容输入到当前的命令行,则可以通过如下方式:

1
2
# 回写最近一次的内容到命令行,是按键上的
C-r //

在替换命令中,同时还可以通过\@={register}来直接访问指定寄存器的内容,比如:

1
:%s/\n/\=@a/g —> 使用a寄存器中的内容替换换行符。

结合上述方案,则可以随时修改寄存器的内容来使替换的命令可重复执行,设置寄存器中的内容的命令如下:

1
:let @{register} = '值'