快捷键和控制序列--bash的命令行编辑原理以及其它杂述

晨曦之光 发布于 2012/04/10 14:59
阅读 424
收藏 1

对于整天在命令行下工作的家伙来说,命令行编辑功能的强弱直接关系到了工作效率,试想如果一个命令行很长很长,写到最后发现很靠前的一个地方写错了,需要修改,此时如果按方向键的话还不累死,因此对于shell来说,非常需要一种行编辑功能,可以在一行当中快速定位。bash就提供了这样的功能,可惜知道这一点的人比较少。首先来有个感性认识,熟悉几个简单且常用的快捷键(每个快捷键都会发送一个键序列,然后shell会解释这个序列然后作出答复)
Ctrl-A:将光标移动到行首
Ctrl-T:交换光标前面的两个字符,可以在命令行模拟冒泡排序
Ctrl-L:clear 清屏
Ctrl-N:删除本行
Ctrl-E:将光标移动到行尾
Ctrl-K:删除从光标到行尾的内容
Ctrl-U:删除从光标到行首的内容
Ctrl-W:删除左边的词
Ctrl-Y:撤消 Ctrl-U Ctrl-K Ctrl-W
Tab:命令补全
Ctrl-R:在命令历史中增量查找
在SecureCRT中增加一个键映射的方式是:
会话选项:终端-仿真-映射键
点击“映射一个键”后出现一个小小对话框,让你使用键盘输入,输入ctrl+f,然后SecureCRT会让你输入你输入刚才的那个组合键时需要发送的字符串,此时就要看一下bash(注意,不是vt100的手册或者别的终端的手册)的手册了,以光标向右移动一个单词为例,bash要求发送ESC+F,那么此时就输入要发送的字符串是"/033f",然后保存,试一下刚刚设置的键映射,在命令行上写一行空格分隔的字符串:ab cd ef,把光标置于行首,按下ctrl+b,看看是不是光标前移了一个单词,实际上更直接的,你可以直接输入ESC键,然后输入f键。
     实际上完全可以不使用ctrl键,直接输入一个f就将光标forward一个单词,只是那样的话如果你想输入字符"b"的话就需要另外想办法了,正如vi/vim那样,输入一个i就进入插入模式,输入esc就退出插入模式,实际上就是更改了键映射(keymap),在命令行也可以这么干的,只是复杂了一些(参考bash的VI_MODE)。总之,编辑命令行的方式是可以定制的,按照自己的习惯设置不同的键盘按键和bash内置的命令序列的映射即可,bash内置的的命令序列是什么,是以ctrl或者esc打头,也就是/01或者/033或者/x1b打头的字符串,后面跟什么?看bash的源码!如果没有我需要的怎么办?自己修改代码自己添加!
     bash中的emacs_keymap.c是一个很重要的文件,bash默认的将keymap设置成了emacs的keymap,其中emacs_standard_keymap和emacs_meta_keymap是两个至关重要的结构体,它们都是KEYMAP_ENTRY的实例组成的数组,KEYMAP_ENTRY的定义如下:
typedef struct _keymap_entry {
  char type;
  Function *function;
} KEYMAP_ENTRY;
数组的下标和键序列一一对应,而function指示bash在接收了这个键序列后的行为,其实就是将bash的控制键序列转化为终端的控制键序列,然后写入终端,比如bash定义的ctrl+L键是清屏,bash收到该键后会写入一个vt100的esc的键序列/033[2J来完成清屏(使用vt100兼容终端的情况下)。到此大家应该都明白,快捷键序列是终端发给shell的,而终端控制序列(比如vt100的esc控制序列)是shell发给终端的,shell可以在快捷键和终端序列之间做映射,做转化,shell做了转化后,原先的快捷键就有可能被解释成了vt100的控制序列,比如ctrl+l快捷键被bash解释成了'/033[2J',然后bash将后者写回终端,结果就是终端清屏。
     如果自己希望的功能在bash中没有被定义怎么办呢?办法就是修改bash,在emacs_standard_keymap数组中添加一个元素,然后将处理回调函数定义成自己希望的vt100功能,比如可以为“将光标定位到特定的位置,即'/033[x;yH'定义一个元素,该元素的回调函数为向终端回写到终端。值得注意的是,所谓的终端控制序列只在终端的所在地起作用,在linux主机上只负责处理逻辑和终端伪终端的请求,这和X Server是一样的,显示只在server端进行,client端只负责告诉server如何显示,类比终端的情形,一个远程终端就相当于一个X server,只是功能弱些罢了,虽然主机通过发送vt100控制序列要求终端做一些动作,比如移动光标,清屏,切换字符集等等,但是终端完全可以自己解释这些控制序列然后作出完全不同或者不合逻辑的行为,还记得linux终端的乱码问题吗?通过设置终端字符集可以使终端乱码,比如echo -ne '/033(0' && echo -ne '/033)0'这个命令写入了/dev/pts/X,控制序列进入pts/X从/dev/ptmx被sshd读出,然后通过socket发送到远端,远端就乱码了,可是通过strace -p [sshd的pid]或者ssh不加密情况下抓包发现,直到sshd将数据从ptmx读出,数据都不是乱码,sshd只是将数据加密,由此可见,数据是到了终端这边才乱掉的,也就是说,“设置字符集”这件事只是一个命令,只有它到了实际终端才会被执行,如果修改一下终端的代码,忽略掉这个命令或者曲解它都可以,这就是将在外,君命有所不受。控制序列在从写入/dev/pts/X到远程(或者本地)终端接收到的过程中,它只是一个命令而已,具体执行不执行以及如何执行完全取决于终端本身。
     所以在linux内核的pty驱动中没有发现任何的对控制序列的解释,可是在vc(vitrua console)的驱动中却有完整的解释,do_con_trol中就解释了很多的控制序列,ESsetG0和ESsetG1两个字符集的切换全在内部执行,这是因为虚拟终端本身就在本机(就是开机后的那些可以通过alt+Fx切换的黑色屏幕),因此像/dev/ttyX之类的虚拟终端内部必须包含对控制序列的执行。
     vt100兼容终端中,除了esc-控制序列外,ctrl+v也会开启一个控制命令,比如切换G0和G1的命令就是ctrl+v/ctrl+o/n来发出的,其命令就是0a0e/0a0f,将之用echo -ne写入一个终端就会切换该终端的字符集,写入的时候,直接将0a0e或者0a0f作为命令写入,这两个字节到了实际终端的时候,终端解释之,执行之,切换字符集...如果终端忽略了它们呢?很简单,那就不切换字符集。如果理解了X系统,那么可以反过来加深对终端的理解,虽然终端早在X系统之前很久就出现了,X系统之所以出现本身就是为了弥补字符终端的不足,字符终端太单调了,其实X服务器有时候也被称作X终端,有的X终端对X客户端发来的命令就不是原样执行的,事实上每一个X终端都会在遵循协议规范的前提下自主解释并执行x客户端的命令,可能会加入一些自己扩展的东西,这也就解释了为何有的x终端很绚。
     vi和bash一样,本质上也包含了一个按键解释器(和emacs_standard_keymap,emacs_meta_keymap类似),这就是为何可以通过esc来切换模式的原因,esc按键或者i按键会触发解释器的切换,从命令模式切换到插入模式或者相反。vi其实和vt100终端控制序列关系不大,它只是一个带有缓冲区的按键解释器而已,它可以将按键在使用vt100终端的情况下解释成vt100的序列,也可以在使用windows系统的情况下解释成windows的GDI函数调用,总之,你的输入是键输入,不要指望你总可以输入一个终端控制序列(除非echo -ne或者一些特殊情况),键输入相当于一个请求,shell或者别的应用程序接收到这个键请求后会根据你的终端类型发送控制命令,这个控制序列指导你的终端如何反应,键在终端本地发出,和本地关联--windows?linux?,而控制命令序列则由主机shell发出,最终由终端本地执行,vt100以esc作为序列开头码完全是为了方便,之所以你输入esc然后输入b(或者发送/033b)会回退一个单词,这和vt100的esc序列丝毫没关系,这是因为你的shell解释了这个esc+b快捷键。


原文链接:http://blog.csdn.net/dog250/article/details/5789018
加载中
返回顶部
顶部