-------------------------------------------------------
作者:
日期: 2005-12-28 -------------------------------------------------------
作为GNU项目之一的BASH(GNU Bourne Again Shell)丰富多彩的命令行编辑模式很大程度上都要归功于GNU Readline Library(以下简称为Readline)。有意思的是,尽管Readline在它man手册的bugs section中注明“It's too big and too slow",但它仍被广泛地使用着:gdb, postgresql, lftp 等等──而且出现了各种包装(wrapper)、扩展(extension)以及实现(implimentation)。
Readline从终端(各种终端,包括虚拟终端)获取用户输入的字符流,辩认其中一些特定的字符序列(characters sequences),然后执行这些序列对应的函数或者宏。比如,在BASH的命令行下面,看到"ctrl+a"(注意,序列本身往往不是这几个字符,这里只是形象说明同时按下ctrl键和a键,下同)序列,就会执行“把光标移到行首”的动作。还有按下”tab“键后会发生什么? 按下"ctrl+d"呢? 我说到的这几个动作,都是bash命令行默认的编辑模式,从BASH的man手册,我们可以看到其他很多好用的默认键盘热键。以下讨论的所有键盘动作,不另外说明的话,都是指在BASH命令行下发生的。
下面我要介绍的是如何自定我们自己的热键。以最简单的例子开始吧!请用ls查看一下你的home目录下面有没有一个 .inputrc 文件? 没有吗,创建一个! ~/.inputrc 就是当前用户的Readline初始化文件,每当用户登录到BASH时,它将被自动用来初始BASH命令行的Readline环境。好了,往.inputrc输入以下东西:
代码:
"z": "free -m" // 注意,冒号要紧跟在 "z"后面。
好了,保存该文件。然后按一下 "ctrl+x ctr+r"(就是先按一下ctrl+x,再迅速按一下ctrl+r。这是BASH默认的重新加载Readline初始文件的热键。当然你可以不用这貌似古怪的东西,直接关闭终端再重新开启,或者直接logout再login都行!)。现在试试你的“z”键吧,看到了什么?对,每按一下“z”,就出现一次“free -m”。 还没完,这有什么用?还得自己敲回车才能看到结果!──这不就像没有遥控器的电视机,还要自己去够开关么?──别急,修改一下 ~/.inputrc :
代码:
"z": "free -m\C-M" // 注意,冒号要紧跟在 "z"后面。
再试试看?哦耶~ 其实这个\C-M 就代表了回车键。
现在再修改~/.inputrc如下:
代码:
"z": "free -m\C-M"
Control-u: "uname -a\C-M"
"\e[A": history-search-backward
看起来有点复杂了。对于第二行,可以猜测,是按下ctrl+u后执行"uname -a"这个命令。但第三行是什么? 其实 "history-search-backward" 是Readline内建的一个函数,而 "\e[A"是方向键“上”对应的字符序列。所以第三行就是按下向“上”键后调用 history-search-backward 函数,其功能是在输入命令时根据已有输入前缀找出以前输入的命令。
另外一个我们要注意的时,这三行的表示形式都略有不同──对了,是引号!在 ~/.inputrc里头,以 “XXX: XXX ”出现的东西就是“键绑定”(key binding)表示法。冒号前面是“绑定名”(就是被绑定的按键对象),冒号后面的是“绑定动作”。
绑定名有两种表示法,带引号和不带引号的:带引号的就是字符形式的键序列,样子看起来往往显得比较怪,就像第三行。(下文还将提到这个“怪”);不带引号的的是符号键名(symbolic key name)形式的键序列,往往以"Control-" 和 "Meta-" 作为前缀。注意,这里的引号必须是双引号!其实很多情况下,这两种形式都是可以互相替换的。比如 Control-u 可以等价为 "\C-u" ,反斜杠("\")在这里有其特殊意义。
绑定动作也有两种表示法,带引号和不带引号的:带引号的是函数名,最常用的是Readline内建的很多函数,当然你也可以建立自己的函数。不带引号的是“宏” ,可以是任意字符串。注意,这里的引号即可以是双引号,也可以单引号!而且,在宏里头如果要包含类似于 斜杠("\"), 引号(") 等等字符时,必须采用以反斜杠("\")方式的转义字符。
另外,在我的系统里注意到两个细节:1、 键绑定形式的冒号必须紧跟绑定名。2、ctrl+x ctrl+r 重新读取 ~/.inputrc的方式和 logout-login后重读取 ~/.inputrc的结果是有不同的。前者只能覆盖先前做的键绑定,而不能消除(比如,你即使从 ~/.inputrc中删除了某个键绑定,但只要不重新定义其绑定对象,则即使是 ctrl+x ctrl+r以后,旧的键绑定仍旧有效)。而login-logout则从 ~/.inputrc全新的定义了键绑定。 这两个细节是不是Readline的bug我也无从知道。
再者,就要说说上面说的带双引号的绑定对象。考虑下面这个东西:
代码:
"\e[14~": "ps -a --forest\C-M" //冒号前面的东西长的真怪,它代表了F4
其结果很简单,按F4后,会执行"ps -a --forest"这个命令。问题是,你怎么知道F4是这副模样?其实这里有个小窍门:在BASH命令行下,先按ctrl+v,再按F4,就知道了。同理,你还可以通过这个办法得知譬如"esc" 、"back-space" 、"del"等等键在特定终端下的 字符序列形式。现在又有一个问题,我用"ctrl+v, F4"显示出来的是"^[[14~"啊,怎么上面的例子写成"\e[14~"?其实,"esc"一般会显示为 "^[",但作为绑定对象时,必须转义为"\e",而"F4"显示为"^[[14~"相当于"esc [14~",替换掉esc后,就成了"\e[14~"了。 再次强调,不同的终端下的按键对应的字符序列往往不同,所以你均可以按下"ctrl+v"后再测试一下实际上应该显示为什么。
说到这里,我们已经知道作为用户(而不是开发者)如何简单的自定键盘热键了。 需要更详细的东西吗?请参考man readline 和 info readline