天天看點

linux下用文本處理器處理二進制檔案後的終端亂碼問題

亂碼!亂碼!在linux終端上不小心或不在意cat了一個elf檔案,或者僅僅是cat了一個word文檔,整個shell就成了亂碼,連提示符都不例外,很多人包括我之前都是索性關掉shell,然後重新打開一個shell,這linux的終端未免也太脆弱了吧,一向宣稱程序間隔離的linux系統,怎能對待如此重要終端如此如此,盈天地之唯美,隻在一草一木之間,然linux卻如何做到。

     事實上,在徹底解決問題之前,我不得不先為linux平反,首先造成終端亂碼的不是linux設計的漏洞,而是使用者的有意為之,另外即使使用者有意為之,如果你換一下終端類型,cat同樣的二進制檔案就可能不再亂碼,最後,要記住,不是linux本身出了問題,問題在于字元集。

     使用者的有意為之怎講?使用者并沒有對終端進行設定啊!是的,使用者沒有顯式的設定,可是卻隐式地進行了設定,此所謂隐式是cat或者vi等“文本”檢視/編輯器對終端進行了設定,想了解這個就必須對終端有一個深入的了解,這裡不再介紹,需要者自然google之。每個終端為了回顯字元必有一個字元集配置,每個終端為了實作功能,必有一個指令解釋規範,對于最古老的vt100終端類型來說也是這樣,如果很多終端對vt100相容,vt100有兩種字元集設定,稱為G0和G1,注意,這G0和G1隻是一個中間層,并沒有指向最後的字元映射表,使用者可以為每一個字元集配置其指向的字元映射表,這字元映射表才最終地進行了字元顯示時的解釋。我們在解決問題之前先來手動制造一次亂碼,解決錯誤之前首先明白為什麼錯。要手動制造亂碼,由剛剛介紹的理論得知,隻要将G0或者G1指向一個錯誤的字元映射表就可以了,但是怎麼做到呢?于是我們就不得不熟悉一下相關的vt100的控制指令了,vt100的控制指令均是以ESC鍵開頭,後面跟一個鍵序列,比如ESC+[+2+J的意思就是清屏,那麼我們測試一下這個清屏指令,用echo -e來測試,esc鍵是0x1b,于是echo -e '/x1b[2J'就實作了清屏,既然知道了vt100的控制指令寫法,我們接下來來一次手工亂碼,還是先來一段理論,很多系統用四張字元映射表,分别設為a,b,c,d,一般a為我們使用的映射表,具體這四張映射表分别有何屬性,本文不再介紹,請google之,關鍵詞為vt100,那麼隻要我們将字元映射表設定為b,c,d就可能造成亂碼了,我們不妨試一下,配置字元映射表的指令是ESC+(B/0/U/K(G0字元集)和ESC+)B/0/U/K(G1字元集),于是我們來一個echo -e '/x1b(0',看看是不是亂碼,如果不是就說明目前的字元集可能是G1,那麼就試試echo -e '/x1b)0',很多人看到的結果已經是亂碼了,這就說明我們手工成功制造了亂碼事件,怎麼改回來呢?十有八九是echo -e '/x1b(B'或者echo -e '/x1b)B',注意以上指令可以用重定向的方式進行,比如echo -e '/x1b(0'>/dev/ttyX等等,否則一旦終端亂碼了,你也就隻能盲打了。

     于是,很多問題不用再解釋了,cat或者vi本來就不是讓你用來處理二進制檔案的,以cat為例,它基本就是下面的處理過程:

while(...) {

    read(...);

    write(..)

}

在write的時候,一旦二進制序列中用上面的echo -e '/x1b(0'(或者U,K)那就麻煩了,cat在你不知道的情況下将'/x1b(0/u/k'寫入了終端,而所有人都不能保證你的二進制檔案中沒有那樣的序列,但是如果那是文本檔案,除了你指明的轉義,一個"/x"一般就被解釋成了一個字元串而不是控制字元,也就是說,文本檔案的任何處理都是使用者指定的,用cat/vi處理二進制檔案本身就是誤用。在你使用vt100終端的情況下,cat /usr/bin/test試一下吧,然後再echo -e '/x1b(B'恢複之,另外在終端上,我們也可以使用快捷鍵,比如ctrl+v+n可以将字元集從G0切換到G1,如果G0和G1的字元映射表設定得不同或者不相容,那麼就會出現亂碼,用ctrl+v+o恢複之,其實這裡的ctrl+v+n/o和/xe//xf是一回事,具體可以檢視一下鍵碼的知識。用vi我們也可以測試一下,vi一個檔案,然後按ctrl+v,表明接下下插入控制字元,之後按ctrl+n,然後正常輸入字元,儲存,退出,然後cat該檔案,亂之!

     好了,這下問題解決了,那麼能否徹底解決這個問題呢?事實上可以的,為了防止二進制檔案中的轉義序列的write寫入将G0切換到G1,那麼就将G0和G1的字元映射表設定成同一個,為了防止二進制轉義序列改變G0或者G1的字元映射表,那麼就在每次處理完的時候重新恢複字元映射表的原始設定,不過,最好額辦法不是為系統增加系統的魯棒性,而是讓各個程式各司其職,不要誤用。還有一種方式就是使用健壯性比較好的終端,比如ansi以及linux終端等等,而放棄使用vt100相容終端,使用rset程式修改之。

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

繼續閱讀