寄存器

Vim 提供了许多寄存器⽤于存放内容,可以理解为剪贴板, ⼀个字符对应⼀个寄存器(例如a-z0-9),不同的寄存器有对应的功能,具体细节非常复杂,下表只列举常用的寄存器。

名称 标识 内容或功能
无名寄存器 " 默认寄存器(上一次复制或删除的内容)
删除寄存器 0-9 文本复制和删除历史
小写寄存器 a-z 手动指定用于保存文本
大写寄存器 A-Z 用于追加内容到同名的小写寄存器
剪切板寄存器 + / * 系统剪贴板(需要 Vim 支持)
空寄存器 _ 删除但不存储(黑洞)

此外,还有一些存放特殊信息的只读寄存器:

  • %:当前文件名
  • .:上一次插入的内容
  • ::上一次执行的命令

通过 :reg {register} 可以查看对应寄存器的内容,使用 :reg 则会查看所有寄存器。

在复制/删除操作前加上"{register}就可以指定本次操作所使用的寄存器,例如:

  • "ayy:复制当前行内容,保存到a寄存器中;
  • "bdiw:删除单词,保存到b寄存器中;
  • "_dd:删除当前行,保存到空寄存器(也就是直接丢弃,不污染剪贴板)

在粘贴时可以指定使用的寄存器,例如:

  • "cp:粘贴,使用c寄存器的当前内容。

注:即使退出 vim,寄存器中的内容通常也不会丢失,而是会存储在 .viminfo 文件中。

宏指的是录制⼀系列键盘操作,并允许我们重放这些操作,常用于批量化操作文本。

基本用法:

  • q{register}:开始录制一段宏,将动作记录在寄存器 register 中;
  • 执行希望记录的动作,按 q 退出录制;
  • @{registetr}:重放寄存器 register 中的动作;

在使用一次 @{registetr} 之后,直接使用 @@ 就可以自动重放上一次宏操作,使用 [count]@{register} 可以自动重放 count 次。

注意:

  • . 命令对宏不⽣效, . 命令只记录上⼀次修改,⽽宏可能包含多次修改。
  • 由于宏的目标是重复操作,在动作序列中需要在开头或结尾跳转到需要编辑的位置,否则重放是错误的。

Command 模式

Ex Command 格式

Ex Command 的完整格式如下:

1
:[range] {excommand} [args]

其中:

  • [range]:指定操作的行范围,缺省时默认为当前行;
  • {excommand}:具体操作命令;
  • [args]:命令附带的参数。

range 与 address 表示

Command 模式下的命令都可以加上对应的行范围 rangerange 由一到两个 address 组成(两个之间用,分隔)。

下面是一些 address 的例子:

  • {lineno}:具体行号,例如 3 代表第3行,0 代表首行之上的虚拟行
  • $:最后一行
  • .:光标所在行
  • /{patttern}/:下一个 pattern 所在行

还支持简单的加减运算,例如:

  • .+3:当前行加上3行
  • $-3:倒数第4行

address 可以组合得到 ranges,例如:

  • 1, 3:1行到3行
  • ., .+4:当前行到后续4行(共5行)
  • $-3, $:倒数第3行到末尾行

除此之外,还有一些特殊范围:

  • %:整个文件的所有行
  • '<,'>:Visual 模式下选中范围的开头和结尾(如果在 Visual 模式下选中范围并输入 :,会自动补充为 :'<,'>

常见的 Ex Command

  • :[range] yank [x]:复制指定行(保存到寄存器 x 中)(yank 可以缩写为 y
  • :delete [x]:删除指定行(保存到寄存器 x 中)(delete 可以缩写为 d
  • :print:打印指定行(print 可以缩写为 p),注意这只是在底部展示一下对应内容,并不是粘贴到文本中。
  • :[range] copy {address}:把指定行插入到 address
  • :[range] move {address}:把指定行复制到 address
  • :[address] put [x]:把寄存器 x 的内容插入到 address 后面(可以插入第0行后面)

例如

  • :yank a:复制当前行,保存到寄存器 a 中;
  • :1, 3 delete a:删除1行到3行,保存到寄存器 a 中;
  • :$-3, $ print:打印倒数第3行到末尾行

Command 批量操作

normal 命令

normal 命令可以对 range 中的所有⾏执⾏ Normal 模式下的命令 commands

1
:[range] normal {commands}

例如:

  • :[range] normal .:⽤ normal 命令在指定的⾏上重复 . 记录的操作(先进行一次修改以记录到 .
  • :[range] normal @{register}:⽤ normal 命令在指定的⾏上重放宏 register

global 命令

global 命令可以对 range 中的所有包含 pattern 的⾏执⾏ Command 模式下的 Ex 命令 commands

1
:[range] global/{pattern}/[cmd]

其中 cmd 缺省时是打印print

例如:

  • :% global /TODO/delete:删除文件中所有包含TODO的行

替换命令

替换命令可以对 range 中的所有包含 pattern 的⾏操作,将 pattern 替换为 string

1
:[range]s/{pattern}/{string}/[flags]

其中的 flags 是可选的,用于调整匹配替换行为的细节:

  • g:替换所有匹配项(默认仅替换第一个匹配项)
  • i:忽视大小写(默认区分大小写)
  • c:每次替换时都要求确认,可以执行、跳过或退出
  • n:计数匹配项,不进行替换

一些例子:

  • :s/abc/xyz:替换当前行的 abc 字符串为 xyz
  • :s/abc/xyz/g:替换当前行的 abc 字符串为 xyz(替换所有匹配项)
  • :%s/abc/xyz/g:替换所有行的 abc 字符串为 xyz
  • :10,20s/abc//gn:计数指定范围内的 abc 字符串

补充

从外部的粘贴

现在考虑在 windows 系统下基于 terminal+ssh 远程操作中的复制粘贴,此时显然无法使用 vim 内部的复制粘贴机制。

对于文本文件的大段粘贴,如果直接ctrl+v会出现很多异常:

  • 例如如果当前不是 Insert 模式,在开头的部分字符可能会被直接丢弃,直到出现 ia 等字符才会进入。
  • 例如可能产生异常的缩进,还可能将某些行自动注释掉。

这是因为vim将字符流视为用户从键盘进行的输入行为,并且视作命令进行处理。

可以通过下面的选项将 vim 设置为粘贴模式,此时就可以保持原样进行粘贴

1
:set paste

在执行ctrl+v之前,最好让 vim 保持在如下模式

1
-- INSERT (paste) --

在粘贴完成后关闭选项即可

1
:set nopaste

复制粘贴的过程实际上非常复杂,vim 和 nvim 的表现并不一致,使用各种 terminal,使用 tmux 都可能带来额外的麻烦,字符流在这些环节中传递,稍不注意就会出错,就像 windows 中的中文乱码一样麻烦。nvim 在这方面比 vim 更加友好,但是也有很多坑。

字母与常见含义关联

字母 常见含义
a append:追加
around:包含边界(文本对象)
b back:返回
block:括号内容
c change:修改
d delete:删除
e end:结尾
f find:行内搜索
g goto:跳转(不能单独使用)
h 左移
help:帮助
i insert:插入
inner:内部(文本对象)
j 下移
join:合并行(J
k 上移
l 右移
m mark:标记
n next:下一个
o open:打开新行
p paste:粘贴
paragraph:段落(文本对象)
q quit:退出(还有录制宏等功能)
r replace:替换(单个字符)
s substitute:替换
sentence:句子(文本对象)
t till:行内搜索(跳转到目标前)
u undo:撤销
v visual:可视化
w write:保存
word:单词
x 删除单个字符
y yank:复制
z 滚动窗口