“怎么退出 vim”是程序员圈子里流传最广的梗之一——一个编辑器要是连怎么关掉都成了笑话,说明它一定有什么地方和你以为的不一样。

这个”不一样”,就是 vim 最核心的概念:模式。搞懂这一件事,剩下的操作都顺理成章。

这是终端效率小系列第二篇,承接 不熟悉的命令怎么快速搞懂:man 页面结构、apropos 搜索与 –help 速查

vim 最核心的概念:模式

大部分编辑器打开就能直接打字。vim 不是——它默认打开的是 Normal 模式(也叫命令模式),这时候敲键盘不是在输入文字,是在下命令。想打字,得先按 i 切换到 Insert 模式;打完字想回去下命令,按 Esc 切回 Normal 模式。

1
2
3
打开 vim → 默认在 Normal 模式(敲字母是命令)
按 i → 切到 Insert 模式(可以打字了)
按 Esc → 切回 Normal 模式(打字模式关闭,字母重新变成命令)

新手最常见的翻车场景就是这个:不知道自己在 Normal 模式,对着屏幕一通乱按,结果文件被改得面目全非——因为那些字母全被当成了命令,不是文字。记住一条:不确定自己在哪个模式,先按 Esc,回到 Normal 模式再说,这是最安全的复位动作。

先学怎么退出

呼应开头的梗,这个必须先讲:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
:q
# 退出。文件没有改动过才能用,改过没保存会报错拒绝退出

:q!
# 强制退出,不保存任何改动——改错了想放弃这次编辑,用这个

:w
# 只保存,不退出,继续编辑

:wq
# 保存并退出,不管文件有没有改动过,都会写入磁盘

:x
# 也是保存并退出,但只在文件真的被改动过时才写入磁盘;
# 如果没有改动,:x 不会碰这个文件,文件的修改时间也不会变
# 日常用 :wq 和 :x 效果几乎一样,区别只在"文件没改动时要不要触碰修改时间"这种边缘场景

所有这些命令都以 : 开头,输入前记得先确认自己在 Normal 模式(如果还在 Insert 模式,先按 Esc)。

Normal 模式下移动光标

vim 不太依赖方向键,用几个字母键代替(键盘老式布局上这几个键离主键位更近,不用挪手,很多人这么解释这个设计,具体历史已经不可考):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
h j k l
# 依次是 左 下 上 右,代替方向键
# 记不住就想 j 像个往下的箭头,k 在它上面对应往上

w
# 跳到下一个单词的开头

b
# 跳到上一个单词的开头(back)

e
# 跳到当前/下一个单词的结尾

0
# 跳到当前行的行首

$
# 跳到当前行的行尾

gg
# 跳到文件开头

G
# 跳到文件结尾

前面加数字表示重复次数:

1
2
3
4
5
5j
# 往下移动 5 行,比按 5 次 j 快

3w
# 往后跳 3 个单词

行内跳转:按字符查找与括号匹配

hjkl/w/b/e 这些是逐字符或逐单词移动,遇到”跳到这一行某个具体符号上”的场景(比如跳到某个逗号、跳到配对的括号),还有更快的办法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
f,
# 向右查找光标所在行里下一个 "," 字符,光标直接停在它上面(find)

t,
# 向右查找下一个 "," 字符,光标停在它前面一位(till,"直到"但不包含)

F,
# 向左查找上一个 "," 字符,光标停在它上面

T,
# 向左查找上一个 "," 字符,光标停在它后面一位

;
# 重复上一次 f/t/F/T 查找,跳到下一个匹配,不用重新敲一次 f 加字符

%
# 跳到光标所在括号 ( { [ 的配对括号上,来回按 % 可以在一对括号之间横跳

一行很长的代码里要定位到某个符号,f 加上那个字符,比一个字符一个字符挪快得多;改代码时经常要确认括号有没有配对齐,把光标放在括号上按 % 直接跳到另一半,比肉眼数括号可靠。

Normal 模式下编辑文本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
i
# 在光标当前位置之前插入,进入 Insert 模式

a
# 在光标当前位置之后插入(append),进入 Insert 模式

o
# 在当前行下面新起一行,进入 Insert 模式

O
# 在当前行上面新起一行,进入 Insert 模式

dd
# 删除光标所在的整行(同时会把这行内容存到剪贴板,等于剪切)

yy
# 复制光标所在的整行(yank,不删除)

p
# 粘贴——把上一次 dd 或 yy 存下来的内容,粘贴到光标下面

u
# 撤销上一步操作

Ctrl-r
# 重做(撤销"撤销")

x
# 删除光标所在的这一个字符——精简版的 dd,只删一个字符不是一整行

r字符
# 把光标所在字符替换成指定字符,替换完自动回到 Normal 模式,不用再按 Esc
# 比如光标停在一个打错的字母上,按 rx 就把它换成 x

cw
# 修改一个单词——删除光标到当前单词结尾的内容,直接进入 Insert 模式等着输入新内容
# 等于先 dw 再 i,但 cw 是更常用的固定搭配,一步到位

.
# 重复上一次的修改操作——改完一处后,把光标移到下一个类似的地方按 .,
# 不用重新敲一遍命令,改多处同样的错误时特别省事

i/a/o/O 这四个进 Insert 模式的方式,区别就在光标最终停在哪:i 停在原地之前,a 停在原地之后,o/O 分别在下面/上面新开一行。

Visual 模式:选中一段范围再操作

前面的编辑命令大多是”整行”或”一个单词”级别,想自己框选一段范围再操作,要用 Visual(可视)模式。进入方式有三种,选中范围的方式不一样:

1
2
3
4
5
6
7
8
v
# 进入字符可视模式,逐字符选中——像鼠标拖选一样,选中范围不受行/单词边界限制

V
# 进入行可视模式,整行整行地选,光标上下移动就是按行扩大选区

Ctrl-v
# 进入块可视模式,选出一个矩形范围,常用来同时给多行的相同列位置加字符

选中范围之后,跟前面学过的编辑命令组合使用,效果作用在整个选区上:

1
2
3
4
5
6
7
8
d
# 删除选中的范围

y
# 复制选中的范围(yank)

c
# 删除选中的范围,直接进入 Insert 模式,改成新内容

举例:用 V 选中三行按 d,一次删掉三行;用 v 选中一段文字(不是整行)按 y,只复制这一小段。选错了想取消,按 Esc 退出 Visual 模式,不会做任何改动。

查找和替换

搜索用 /,和上一篇讲 man 页面查找less 的搜索快捷键是同一套逻辑——vim 的分页和搜索机制本来就深受这套习惯影响:

1
2
3
4
5
6
7
8
/关键词
# 向下搜索,回车确认

n
# 跳到下一个匹配

N
# 跳到上一个匹配(大写,方向和 n 相反)

全局替换用 :%s

1
2
3
4
:%s/旧文本/新文本/g
# % 表示作用范围是整个文件(不加 % 只替换当前光标所在行)
# s 是 substitute,替换命令本身
# g 表示每一行里所有匹配都替换,不加 g 的话每行只替换第一个匹配到的

%g 这两个符号经常被忽略,结果就是”明明替换了,怎么还有没改的地方”——十有八九是漏了 %(只在当前行生效)或者漏了 g(每行只替换了第一处)。

终端效率小系列:下一篇

下一篇讲 Emacs 风格快捷键——包括一个进阶技巧:在 shell 命令行敲了一半的命令,直接甩进 vim/nvim 里编辑,改完存盘退出,编辑结果自动回到命令行。用到的正是这篇学的基础操作。