Logo
Section 1 / 6

Readline 库(checkpoint)

在本节课中,你将学习 readline 是什么,没有 readline 时 bash 的样子,以及如何操作和深入了解其功能特性。

检查点

Readline 库(checkpoint)

在本节课中,你将学习 readline 是什么,没有 readline 时 bash 的样子,以及如何操作和深入了解其功能特性。

Readline 是一种被广泛使用却常常被人忽视的技术。在本课中,我将让你了解它,并介绍一些相关概念和使用示例,以便你能应对由其使用或误用引发的任何问题。

这节课有多重要?

编写 Bash 脚本并不需要了解 Readline。但理解 Readline 能极大地帮助你掌握终端和命令行中的实际运作机制。

没有 Readline 的 Bash

首先,我们来看看没有 Readline 时 Bash 的样子。

在你的“正常” Bash shell 中,连续按两次 TAB 键。你应该会看到类似这样的提示:

1Display all 2335 possibilities? (y or n)

这是因为 Bash 默认启用了 自动补全 功能,当你连续按两次 TAB 时,它会列出所有可用的命令。

输入 n 退出自动补全模式。

同样地,如果你多次按下上箭头键,之前执行过的命令应该会重新出现在命令行中。

现在输入以下命令:

1bash --noediting
Terminal

--noediting 标志启动 Bash 时禁用 Readline 库。此时再次按两次 TAB,你会发现结果不同:shell 不再识别你的 Tab 键,而是直接将制表符输出到屏幕上。自动补全功能已失效。

自动补全是 Readline 库提供给终端的众多功能之一。你可以尝试像之前那样按上下箭头键,会发现这些操作也不再起作用了。

按回车键获取一个新的命令行,然后退出这个未启用 Readline 的 Bash shell:

1exit

其他快捷键

当 Readline 启用时,你会拥有大量便捷的快捷键。接下来我快速介绍四个最常用的快捷键,然后再说明如何进一步了解更多信息。

1echo 'some command'

这应该不会带来太多意外。现在:

  • 按下上箭头键,你会看到上一条命令被恢复到当前行
  • 如果你喜欢,可以重新运行这条命令,但在按回车前,你还可以用 Readline 做更多事
  • 按住 Ctrl 键的同时按下 a,光标将跳转到行首
  • 这种多键组合输入的表示方式可写作 \C-a。其中 \C 表示 Ctrl 键,-a 表示同时按下 a 键
  • 接着按下 \C-e(即 Ctrl + e),光标将移动到行尾。我每天都会用到这两个快捷键数十次
  • 另一个非常实用的是 \C-l,它清空屏幕但保留命令行内容
  • 最后一个我要展示的功能是:在输入时搜索历史命令。按下 \C-r,然后输入 ec。你应该能看到刚才执行过的 echo 命令,显示如下:
1(reverse-i-search)`ec': echo echo
  • 再次按下 \C-r,并持续重复
  • 你会看到所有包含 ec 的历史命令(如果你的历史记录中只有一次 echo 命令,那么只会显示一条)
  • 当你看到目标命令时,光标会定位到该历史位置,你可以上下移动浏览,也可以直接按回车重新执行

以下是常用快捷键的参考表格:

Readline 快捷键效果
\C-a移动到行首
\C-e移动到行尾
\C-h删除前一个字符
\C-l清屏
\C-r查看历史命令

注意:什么是 历史记录?历史记录是你在 Shell 中曾经执行过的命令列表。我们将在后续课程中详细讲解。

Readline 提供的快捷键远不止这些。接下来我将教你如何查看全部可用的快捷键。

使用 bind 查看 Readline 快捷键

输入以下命令:

1bind -p

你会看到 Readline 支持的所有绑定列表。数量非常多!如果你感兴趣,可以仔细阅读,但不必立刻理解全部内容。

如果输入:

1bind -p | grep C-a

你将找到之前用过的“行首”绑定,以及我提到的 \C-a 表示方式。

此时你可以尝试查找之前用过的 \C-e 和 \C-r 的绑定。

如果你想完整查看 bind -p 的输出,还需要知道以下几点:

  • \M 代表 Meta 键(也称为 Alt 键)
  • \e 代表键盘上的 Esc 键。Esc 键的绑定方式与其他不同:你需要先按 Esc,再按另一个键
    • 例如,先按 Esc,再按 ? 键,也会尝试自动补全你正在输入的命令。这在 bind -p 输出中被记录为:
1"\e?": possible-completions

Readline 与终端设置

如果你浏览过 Readline 提供的功能,可能会注意到我们之前提到的 \C-r 绑定:

1"\C-r": reverse-search-history

你可能还注意到还有一个绑定允许向前搜索历史记录:

1"\C-s": forward-search-history

我经常遇到的情况是:反复按 \C-r,结果翻得过快,错过了想要的命令。这时我可能会尝试按 \C-s 向前搜索,以找回漏掉的命令。

但请注意!按 \C-s 可能对你无效。

既然绑定存在且 Readline 已启用,为什么会这样?

原因在于:\C-s 在到达 Readline 库之前,已经被终端设置捕获了。

你所使用的终端程序可能具有默认设置,在某些快捷键触发时会执行其他操作,从而阻止 Readline 接收到这些按键。

输入以下命令:

1stty -a

你会看到类似如下的输出:

1speed 38400 baud; rows 24; columns 101; line = 0; 2intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = M-^?; eol2 = M-^?; swtch = <undef>; 3start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W; lnext = ^V; discard = ^O; 4min = 1; time = 0; 5-parenb -parodd -cmspar cs8 hupcl -cstopb cread -clocal -crtscts 6-ignbrk brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl ixon -ixoff -iuclc ixany imaxbel 7iutf8 8opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0 9isig icanon iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt echoctl echoke -flusho 10-extproc

在第二行中,可以看到终端在 Readline 之前会处理的一系列按键绑定。这里的 ^ 字符(称为“脱字符”)代表 Ctrl 键,这与我们之前用 \C 表示的方式一致。

如果你觉得这很混乱,我完全同意。不幸的是,在 Unix 和 Linux 的历史文档中,对这些按键的描述方式并不统一。

如果你遇到终端选项在 Readline 之前捕获了某个快捷键的问题,可以使用 stty 命令取消该绑定。在此例中,我们要取消“停止”绑定。

如果你处于相同情况,请输入:

1stty stop undef

现在重新运行 stty -a,你会发现按键绑定列表变为:

1[...] 2intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = M-^?; eol2 = M-^?; swtch = <undef>; 3start = ^Q; stop = <undef>; susp = ^Z; rprnt = ^R; werase = ^W; lnext = ^V; discard = ^O;

其中 stop 项现在显示为 <undef>。

有趣的是,对我来说,C-r 也被绑定为“重打”功能(即 ^R)。但在我使用的终端中,这个绑定并不会干扰历史搜索。至于为何如此,我尚未弄清。我猜测现代终端不再需要“重打”功能,因此这类绑定被忽略。

顺便提一下这段列表中的其他几个常用绑定:

1intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = M-^?; eol2 = M-^?; swtch = <undef>; 2start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W; lnext = ^V; discard = ^O;
  • \C-c:中断正在运行的程序,终止它
1sleep 99 # 现在按下 \C-c

按下 \C-c 后,你应该看到输出:

1^C
  • \C-z:暂停程序,之后可以用 fg 命令将其恢复到前台继续运行
1sleep 10 # 现在按下 \C-z 2fg 3sleep 10
  • \C-d:发送“文件结束”字符。常用于告知程序输入已结束。在 Bash shell 中输入它,当前的 Bash 会话将关闭。

  • \C-w:删除光标前的一个单词

这些都是终端在将按键传递给 Readline 之前就处理的最常用快捷键。

你学到了什么

  • 什么是 Readline 库
  • 它提供了哪些功能
  • 如何进一步了解这些功能
  • 终端选项与 Readline 的处理顺序
  • 一些终端原生支持的快捷键
  • 标准文档中如何表示组合键

下一步做什么?

掌握了 Readline 库和终端设置的工作原理后,你已经准备好学习更复杂的主题:终端码。

练习题

1)研究 stty -a 列出的终端选项快捷键及其含义。

2)研究 inputrc 文件,并编辑它来配置 backward-kill-word 的调用方式。

Quiz

单选题

\e 指的是

1 / 3

脚本编写 Bash

终端代码