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
--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输出中被记录为:
- 例如,先按 Esc,再按
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 指的是