天天看點

快捷鍵和控制序列--bash的指令行編輯原理以及其它雜述

對于整天在指令行下工作的家夥來說,指令行編輯功能的強弱直接關系到了工作效率,試想如果一個指令行很長很長,寫到最後發現很靠前的一個地方寫錯了,需要修改,此時如果按方向鍵的話還不累死,是以對于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快捷鍵。

 本文轉自 dog250 51CTO部落格,原文連結:http://blog.51cto.com/dog250/1271778

繼續閱讀