4.3 vi 编辑模式实战

与 Emacs 编辑模式相比,vi 编辑模式为我们提供了更多的控制命令。相应地,在 vi 编辑模式下,我们能够操作的粒度也将更细。如果你以前从未使用过 vi 编辑模式的话,那么不妨在亲自体验一番之后再来作决定是否要继续用它。跟 vi 文本编辑器一样,Shell 的 vi 编辑模式也包含两种模式:插入模式和命令模式。在插入模式下,我们输入的字符为字符本身,并没有什么特殊含义,如:h 就是 h;而在命令模式中,我们所输入的字符则为用来执行编辑过程的命令,如:h 用来向左移动一个字符。默认情况下,我们进入的是插入模式。若是要进入命令模式,则需要我们按 Esc 键。在使用 vi 编辑模式时,我们有时候可能会感到迷糊,此刻到底处于哪种模式呢?遇到这种情况,不妨先按 Esc 键 回到命令模式再作进一步的操作。或许这算是选择 vi 编辑模式的小小代价吧。

4.3.1 移动命令

在 vi 编辑模式中,我们可以使用的光标移动命令参考表 4.4

表 4.4: vi 模式移动命令
命令 作用
h 向左移动一个字符
l 往右移动一个字符
b 向左移动一个单词
w 往右移动一个单词
e 移到单词结尾
B、W、E bwe 类似,按不同的单词定义进行移动
0 移到行首
^ 移到行首,但第一个字符为非空白字符
$ 移到行尾

让我们输入一行命令来试试这些 vi 编辑模式中的移动命令:

xiaodong@codeland:~$ echo hello, this is a command

首先,按 Esc 键进入命令模式,此时光标位于 command 结尾的 d 上。

其次,通过 hl 按一个个字符左右移动很直白,无需我们多言。值得讨论的是 bwe 跟它们对应的大写形式的区别:简而言之,bwe 将停留在空白或标点符号处(如该命令行中的 ,(逗号)),而 BWE 则仅仅停留在空白处。例如,我们按 b 会经过 ,,而按 B 将跳过 ,。假如你想要移动更快的话,那么可以用大写字母的命令。而小写字母命令则对于更细粒度的移动有用,比如路径名这种情形。

最后,0^ 都是移到命令行的开头,不过其差异是 0 的开头可以为空白,而 ^ 的开头则不允许。例如:

xiaodong@codeland:~$  cd /var/log/nginx

0 移到开头的空格,按 ^ 移到 cdc 上,按 $ 移到行尾的 x

4.3.2 重复命令

在 vi 命令模式下,每个移动命令之前可以跟一个数字,用来将该命令重复执行多次。例如,3b 表示向左移动 3 个单词,5l 则表示往右移动 5 个字符。值得注意的是,因为 0 本身也是一个命令,所以将它放在命令前面是无效的重复计数。

4.3.3 添加文本

我们已经知道通过按 Esc 键可以进入 vi 命令模式,但是,在命令模式下又如何回到插入模式呢?你只需参考表 4.5 中的命令来执行即可。

表 4.5: vi 模式添加文本的命令
命令 作用
i 在光标左边插入新的文本内容
a 在光标右边追加新的文本内容
I 在行开头插入新的文本内容
A 在行结尾追加新的文本内容

请看例子:

xiaodong@codeland:~$ hello vi

假如我打算将该命令改为

xiaodong@codeland:~$ echo hello, vi editing mode

那么可按以下方式执行编辑操作:

2h 将光标移到空格处,接着按 i 进入插入模式,然后输入新的 ,。按 Esc 键回到命令模式后,继续按 I 以便在命令行开头插入 echo。再次按 Esc 键进入命令模式,最后按 A 在命令行的尾部追加 editing mode

4.3.4 删除文本

利用 vi 模式提供的删除命令,我们不仅可以删除字符,而且也能删除单词,甚至整个命令行。这些删除命令参考表 4.6

表 4.6: vi 模式删除文本的命令
命令 作用
x 删除光标下的字符
X 删除光标左边的字符
dm m 为某个移动指令,如 db 删除光标左边的单词
D 从光标处删除到行尾
dd 删除整行内容

跟移动命令一样,在上述删除命令之前也可以带一个数字,以便多次执行该命令。例如,5x 将删除 5 个字符,而 3dw 将删除 3 个单词,这里 3 的顺序并不重要,d3w 仍然同样有效。

通过删除命令删除的内容,Shell 并没有丢弃,而是将其保留在了删除缓冲器中。稍后,我们可以执行 u 命令来恢复这些删除的内容。如果想要恢复更早时间删除的内容,则只需按 u 键多次即可。

另外一种更有用的方式是复制和粘贴。这样,我们能够在保留原有内容的同时,再储存一份拷贝,以便后续使用。vi 模式中复制及粘贴的命令参考表 4.7

表 4.7: vi 模式复制及粘贴命令
命令 作用
ym m 为某个移动命令,如yw 用来复制光标右边的单词
p 在光标右边粘贴文本
P p 的大写形式,在光标左边粘贴文本

在下面的例子中,我们将看到上述命令的用法:

xiaodong@codeland:~$ echo command-line interface

先按 Esc 键进入命令模式,此时光标位于结尾的 e 上。按 x 将删除 e,按 X 将删除 a,按 db 将删除 interface 剩下的部分(只剩下字符 c),按 dd 则把整行内容都删掉。按 u 又还原刚删除的内容。

4.3.5 替换文本

当我们需要替换命令行中的内容时,除了在删除该内容后再进入插入模式重新输入外,也可以使用 vi 编辑模式所提供的文本替换命令。这些命令组合了删除与插入操作,用起来将更加直接。vi 编辑模式提供的文本替换命令参考表 4.8

表 4.8: vi 模式替换文本的命令
命令 作用
cm m 为某个移动命令,如 cw
C 从光标处删除到行尾,并进入插入模式
cc 删除整行,并进入插入模式
r 替换光标下的字符
R 进入替换文本模式
s 利用输入的字符来替换光标下的字符

要想搞明白这些替换命令如何工作,不妨来试试以下编辑练习:

xiaodong@codeland:~$ echo talk is cheap. show me the kode.

同样的,我们按 Esc 键先进入命令模式,按 cb 将 kode 删除后进入了插入模式,我们输入新的内容 code。再次按 Esc,接着按 4b 左移到 show,按 r 将 s 替换成 S。

4.3.6 搜索字符

vi 编辑模式还提供了一组命令用于搜索命令行中的字符。利用这些命令,我们可以移动光标到特定的字符上。此外,将其跟 dc 命令组合使用,还能够删除或更改从光标处到该字符的这一段文本。这些用于搜索字符的命令参考表 4.9

表 4.9: vi 模式搜索字符的命令
命令 作用
fc 移动光标到 c 的下一处
Fc f 相反方向搜索,移动光标到 c 的上一处
tc 移动光标到 c 左边的字符
Tc 移动光标到 c 右边的字符
; 重复上次的 fF 命令
, 以相反的方向重复上次的 fF 命令

在下面的练习中,我们可以尝试上述字符搜索命令的用法:

xiaodong@codeland:~$ echo A program which handles the interface

在按 Esc 键进入命令模式后,按 fp 光标移到了 p 上,按 th 移到了 h 左边的 w。按 Fm 光标左移到 m 上。我们还可以试试其它的命令,以便熟悉这些命令的用途。

4.3.7 vi 编辑模式总结

从前面我们所讲的内容来看,vi 编辑模式比 Emacs 编辑模式提供了更多的编辑命令。乍一看,似乎很复杂。我们除了勤加练习以期熟悉这些编辑命令之外,还可以总结出以下规律,从而帮助我们更好的加深理解。如图 4.2 所示。

  • 跟 Emacs 编辑模式一样,我们同样可以按照字、“词”、行这 3 个维度来梳理操作命令
  • 删除命令 d 和更改命令 c 能够与移动命令组合使用
  • 移动命令、删除命令、更改命令之前可以加数字用来多次执行
vi 编辑模式图解

图 4.2: vi 编辑模式图解