4.2 Emacs 编辑模式实战

4.2.1 按字移动和删除

让我们先在命令行输入一些字符,然后来看看如何一个一个的移动字符:

xiaodong@codeland:~$ echo 像骇客一样

糟糕,我将“黑客”错输成了“骇客”,现在我要将光标向左移到“客”字,接着删除“骇”字,然后再重新输入正确的“黑”字。

对于命令行新手来说,可能会使用左方向键(←)向左移动字符。好,我们来试一下。按 3 下左方向键(←),于是光标停留在“客”字上。此时,我们再按退格键则删除光标左边的“骇”字。我们接着重新输入正确的“黑”字。因为我们还要继续输入新的内容,所以按 3 下右方向键(→)往右移动光标,直到行尾。之后,我们能够再输入新的内容“使用命令行”。

在上面的操作中,我们通过分别按左方向键(←)右方向键(→)来向左或往右移动一个字符。利用 Emacs 编辑模式,其实有比按左右方向键更好的操作方法。因为左右方向键通常远离键盘中心区域,所以与 Emacs 编辑模式提供的操作方法比起来在效率上会有所降低。

下面,我们就用 Emacs 编辑模式所提供的操作方法来一个个的移动字符。在 Emacs 编辑模式中,向左移动一个字符可以按 Ctrl + b。因此,我们按 3 次 Ctrl + b,此时光标位于“客”字上。同样的,我们通过按退格键来删除“骇”字。在输入“黑”字后,我们需要将光标移到最右边。现在,我们可以按 Ctrl + f,以便往右移动一个字符。需要往右移动几个字符就按几下 Ctrl + f。顺便说一句,在后面的内容中,我们会讲到如何一下子就移到最右边的操作方法。此刻,就先让我们熟悉按字移动的操作吧。

上述操作中我们使用退格键删除光标左边的字符。假如我们向左多移动了一个字符,那么此时光标将在“骇”字上。要想删除“骇”字,难道再往右移动吗?当然不必。Emacs 编辑模式为我们提供了 Ctrl + d 来删除光标下的字符。

在此,我们再介绍一组快捷键:Ctrl + t。这组按键的作用是将光标左边的两个字符交换顺序。例如,当我在输入

xiaodong@codeland:~$ sl

后,通过按下 Ctrl + t 便能够将其变成 ls

xiaodong@codeland:~$ ls

总结起来,按字移动和删除的操作方法参考表 4.1

表 4.1: Emacs 模式按字移动和删除的操作方法
按键 作用
Ctrl + b 向左移动一个字符
Ctrl + f 往右移动一个字符
退格键 删除光标左边的字符
Ctrl + d 删除光标下的字符
Ctrl + t 将光标左边的两个字符交换顺序

4.2.2 按“词”移动和删除

按字符移动和删除毕竟有些琐碎,为了完成某个操作,有时候需要我们按下很多次快捷键。有鉴于此,Emacs 编辑模式针对“词”这个更大的范围提供了移动和删除的操作方法。值得注意的是,我们在此给“词”添加了引号。换句话说,这里的“词”是 Shell 所理解的“词”的含义,与现实生活中所谓的词含义并不相同。在 bash 中,“词”即字母和数字的组合,例如 file1 这种,其中并不包含特殊字符在内。而在 zsh 中,对“词”的界定跟 bash 又有所不同,除了字母和数字之外,还包括比如 *?_- 等这样的符号。通过变量 WORDCHARS,我们可以看到这类字符到底有哪些。在我的系统中,执行

xiaodong@codeland:~$ echo $WORDCHARS

后,可以见到有如下字符:

*?_-.[]~=/&;!#$%^(){}<>

如果你想要让 zsh 判定“词”的行为跟 bash 一样的话,那么不妨将 WORDCHARS 变量的值设为空:

xiaodong@codeland:~$ WORDCHARS=

下面我们来看一个按“词”移动和删除的操作例子:

xiaodong@codeland:~$ grep 'figlet' /var/log/pacman.log

在我输入该命令行后,忽然想到应该查询 lolcat 这个包。为了将 figlet 改成 lolcat,我按 Alt + b 向左一个“词”一个“词”的移动。按 5 下后,此时光标停留在 figletf 上。接着,我按 Alt + d 删除光标右边的“词”(也就是 figlet)。然后输入新的内容 lolcat

现在,如果不需要再输入内容的话,则可以直接按回车键执行命令。但因为我还想将标准输出的内容保存起来,所以继续按 Alt + f 来往右按“词”移动。当抵达最右边时,再输入新的内容 > /tmp/output.log。此刻,我们的命令行是:

xiaodong@codeland:~$ grep 'logcat' /var/log/pacman.log \
> /tmp/output.log

最后,我按 Alt + 退格键(或 Ctrl + w)删除光标左边的 log,并输入 txt 来完成该命令行的编辑。

正如前面所述,在 zsh 中按“词”移动的行为跟 bash 略有不同。这是因为它们对“词”界定的含义不一样的缘故。我们仍旧以上面的例子来加以说明。在光标都位于该命令行最右边的情况下,bash 中按 Alt + b 向左移动到的是 txt 中开头的 t 上;而在 zsh 中同样的操作却移动到了 /tmp/output.txt 开头的 /。在此,我们可以发现 zsh 将 /. 都看作“词”的一部分。两个 Shell 比较起来,zsh 移动更快,按键也更少,但是粒度却要粗一些。因为 zsh 对“词”的界定范围比 bash 更宽,所以对“词”的删除内容也更多。bash 中按 Alt + d 删除的是 txt,而 zsh 中删除的却是 /tmp/output.txt

需要特别指出的是,通过 Alt + 退格键Alt + d 删除的内容,Shell 并没有丢弃,而是将其保存在 kill ring 中。你可以将 kill ring 看作一个特殊的剪贴板。当然,它里面的内容我们也是可以获取的。我们只需按 Ctrl + y 即可获取上次删除的内容。

此外,与 Ctrl + t 交换光标左边的两个字符相似,Alt + t 能够用来交换光标左边两个“词”的顺序。例如:

xiaodong@codeland:~$ echo bar foo

当我们按 Alt + t 后,该命令行将变成:

xiaodong@codeland:~$ echo foo bar

以下介绍的几组快捷键用于更改“词”的大小写。例如,我们在命令行输入

xiaodong@codeland:~$ echo foo

并按 Alt + b 将光标移到 f 上后,此时,按 Esc + cfoo 变成 Foo;若是按 Esc + u,则将变为 FOO;最后,按 Esc + l,又将成为 foo

按“词”移动和删除的操作方法参考表 4.2

表 4.2: Emacs 模式按“词”移动和删除的操作方法
按键 作用
Alt + b 向左移动一个“词”
Alt + f 往右移动一个“词”
Alt + 退格键 删除光标左边的“词”
Ctrl + w 同上
Alt + d 删除光标右边的“词”
Ctrl + y 获取上次删除的内容
Alt + t 交换光标左边两个“词”的顺序
Esc + c 将光标右边的“词”的开头字母变成大写
Esc + u 将光标右边的“词”全部更改为大写字母
Esc + l 将光标右边的“词”全部更改为小写字母

4.2.3 按行移动和删除

比“词”范围更大的移动及删除操作是行。相对“词”而言,我们只需要更少的按键即可移到更广的区域。由此,也将删除更多的命令行内容。让我们通过一个例子来说明:

xiaodong@codeland:~$ grep 'set' *.txt

在此,我想找出当前目录中包含 set 的所有文件。假如我想将这行命令改成:

xiaodong@codeland:~$ egrep 'set' *.md

首先,我按 Ctrl + a 将光标移到该命令行的行首(也就是最左边)。接着输入 e 将命令改成 egrep。然后,我再按 Ctrl + e 将光标移到此行的结尾处(也就是最右边)。在通过 Alt + 退格键 删除 txt 后,重新输入 md 即可完成对该命令行的修改。

下面,我们来看看针对行的删除操作。依然请出我们的老朋友 foobarbaz

xiaodong@codeland:~$ echo foo bar baz

我们先按两下 Alt + b 将光标移到 barb 上。现在,如果我们打算删除 barbaz,那么只要按 Ctrl + k;反之,但假设我们想要删除 echofoo 的话,则需按 Ctrl + u。值得说明的是,在 zsh 中,Ctrl + u 的行为与 bash 中并不相同,它是删除整行的全部内容。

按行移动和删除的操作方法参考表 4.3

表 4.3: Emacs 模式按行移动和删除的操作方法
按键 作用
Ctrl + a 将光标移到行首(最左边)
Ctrl + e 将光标移到行尾(最右边)
Ctrl + k 从光标处往右删除至行尾
Ctrl + u 从光标处向左删除至行首

4.2.4 Emacs 编辑模式总结

从前面我们所讲的内容来看,Emacs 编辑模式的内容编辑范围主要包括下列 3 种:

  1. “词”

针对每一种范围,又包含两种编辑操作,分别为移动和删除。如图 4.1 所示。

Emacs 编辑模式图解

图 4.1: Emacs 编辑模式图解