咱們先來講一下出現亂碼的緣由。html
先舉個實際的例子,咱們通常經過ssh遠程到服務器上進行操做。當在終端上執行一些有輸出的任務時,有可能會遇到亂碼,特別是輸出中有中文時。linux
好比,我登錄上oracle數據庫服務器上,查看oracle RAC的狀態:shell
上面的例子,除了英文字母外其它的都成了亂碼了。 固然這個與運行什麼程序沒有什麼關係,你能夠試一下系統自帶的命令,當參數錯誤時也會也現亂碼。數據庫
當咱們在網上找問題的解決方法時,有讓你修改配置文件的,有讓你修改環境變量的,有讓你換個客戶端的,還有讓你裝語言包的。windows
有這麼多答案供你選擇,但都沒有告訴你緣由。 在給出答案以前我所先分析一下緣由,爲何讓我改環境變量,而不是其它的,它表明了什麼?bash
固然爲了避免刨根問底,我設定一下範圍,只關心繫統層面,語言實現和架構層面咱們就不涉及了。服務器
在Linux裏面有兩個概念與這個問題有關:國際化(相對應的是本地化)、編碼架構
在程序設計層面,輸出有亂碼的程序都是好程序,至少它考慮到了它的用戶可能不是本國人,可能有不懂英文、德文的人在使用。oracle
在不一樣的語言環境下,程序的輸出會有不一樣。與之相關的細節則是環境變量LANG,系統命令locale等。ssh
編碼是數據(文本)在計算機的內部表示。在數據存儲(文件)或傳輸過程當中會以某種規則進行編碼(編碼規範:UTF-八、GBK等)
能夠這樣理解,文本文件都是有編碼的,這個概念在Windows中被弱化了。在中文版的Windows中的txt文件都是以gb2312編碼的。找一個能夠選擇編碼模式的編輯器好比「宇宙第一IDE"VSCODE,若是以utf-8打開gb2312編碼的txt文件,就會顯示亂碼。
終於到關鍵點了,程序輸出的亂碼本質上和文件亂碼是一回事。
以編碼格式」A「寫入文件,以編碼格式"B「解析文件內容並顯示
程序以系統要求(LANG)的編碼格式"A"輸出文本,輸出的內容被」終端「程序以本身的編碼格式」B"解析並顯示。
」終端「程序的編碼格式比較好理解,咱們放幾個截圖就解釋了。Linux系統內的約定比較很差理解,稍後咱們再來看LANG, env, 以及locale.
Linux下的終端程序輸出的編碼格式
Linux下的終端程序gnome-terminal
經過菜單Terminal->Preferences->Profiles->Edit->Compatibility->Encoding進入相應配置界面
除了windows自帶的cmd以外,其它終端均可以更改當前程序輸出的編碼。
簡單地說它是經過環境變量LANG來設定的,系統中的全部程序(包括gnome-termial)在啓動時都會讀取這個值來設定當前的程序菜單、界面、輸出等編碼格式。
能不能顯示可能的確與字體包有關,好比redhat下的rpm包fonts-chinese-3.02-12.el5。它裏面主要是一些字體文件,這些在windows裏也能找到。
[root@newhis1 ~]# rpm -ql fonts-chinese /usr/share/fonts/chinese/TrueType/fonts.cache-1 /usr/share/fonts/chinese/TrueType/fonts.dir /usr/share/fonts/chinese/TrueType/fonts.scale /usr/share/fonts/chinese/TrueType/ukai.ttf /usr/share/fonts/chinese/TrueType/uming.ttf /usr/share/fonts/chinese/fonts.cache-1 /usr/share/fonts/chinese/misc /usr/share/fonts/chinese/misc/fonts.alias /usr/share/fonts/chinese/misc/fonts.cache-1 /usr/share/fonts/chinese/misc/fonts.dir /usr/share/fonts/chinese/misc/fonts.scale /usr/share/fonts/chinese/misc/taipei16.pcf.gz /usr/share/fonts/chinese/misc/taipei20.pcf.gz /usr/share/fonts/chinese/misc/taipei24.pcf.gz /usr/share/fonts/chinese/misc/vga12x24.pcf.gz /usr/share/fonts/zh_TW /usr/share/fonts/zh_TW/TrueType /usr/share/fonts/zh_TW/TrueType/bsmi00lp.ttf
咱們假設這些包都裝過了,也就是說在裝系統時的「語言支持」裏面選過中文了。
Linux支持的語言編碼,經過locale命令能夠查看。咱們只關注英文和中文,因此過濾了一下。
[root@newhis1 ~]# locale -a|grep '^[z|e][h|n]'|grep \\.|grep -v iso en_AU.utf8 en_BW.utf8 en_CA.utf8 en_DK.utf8 en_GB.utf8 en_HK.utf8 en_IE.utf8 en_IN.utf8 en_NZ.utf8 en_PH.utf8 en_SG.utf8 en_US.utf8 en_ZA.utf8 en_ZW.utf8 zh_CN.gb18030 zh_CN.gb2312 zh_CN.gbk zh_CN.utf8 zh_HK.big5hkscs zh_HK.utf8 zh_SG.gb2312 zh_SG.gbk zh_SG.utf8 zh_TW.big5 zh_TW.euctw zh_TW.utf8
環境變量LANG的內容的格式爲: <語言>_<地域>.<字符集>
環境變量LANGUAGE的格式爲:<語言>_<地域>
還有命令locale輸出的LC_開頭的環境變量,它們會對程序的輸出和界面產生響應。
細節能夠參考網文:locale的設定中LANG、LC_ALL、LANGUAGE環境變量的區別
爲了防止連接損壞打不開,我抄一小段:
locale把按照所涉及到的文化傳統的各個方面分紅12個大類,這12個大類分別是: 1、語言符號及其分類(LC_CTYPE) 2、數字(LC_NUMERIC) 3、比較和排序習慣(LC_COLLATE) 4、時間顯示格式(LC_TIME) 5、貨幣單位(LC_MONETARY) 6、信息主要是提示信息,錯誤信息,狀態信息,標題,標籤,按鈕和菜單等(LC_MESSAGES) 7、姓名書寫方式(LC_NAME) 8、地址書寫方式(LC_ADDRESS) 9、電話號碼書寫方式(LC_TELEPHONE) 10、度量衡表達方式 (LC_MEASUREMENT) 11、默認紙張尺寸大小(LC_PAPER) 12、對locale自身包含信息的概述(LC_IDENTIFICATION)。 Locale是軟件在運行時的語言環境, 它包括語言(Language), 地域 (Territory) 和字符集(Codeset)。一個locale的書寫格式爲: 語言[_地域[.字符集]]。徹底的locale表達方式是 [語言[_地域][.字符集] [@修正值]。zh_CN.GB2312=中文_中華人民共和國+國標2312字符集。 locale的設定: LC_ALL和LANG優先級的關係:LC_ALL > LC_* >LANG 1、若是須要一個純中文的系統的話,設定LC_ALL= zh_CN.XXXX,或者LANG=zh_CN.XXXX均可以。 2、若是隻想要一個能夠輸入中文的環境,而保持菜單、標題,系統信息等等爲英文界面,那麼只須要設定 LC_CTYPE=zh_CN.XXXX,LANG=en_US.XXXX就能夠了。 3、假如什麼也不作的話,也就是LC_ALL,LANG和LC_*均不指定特定值的話,系統將採用POSIX做爲lcoale,也就是C locale。 LANG和LANGUAGE的區別: LANG - Specifies the default locale for all unset locale variables LANGUAGE - Most programs use this for the language of its interface LANGUAGE是設置應用程序的界面語言。而LANG是優先級很低的一個變量,它指定全部與locale有關的變量的默認值
當環境變量LANG, LANGUAGE, 和LC_開頭的變量的設定與terminal程序的設定不一致或字符集不包含時就會出現亂碼。
好比,LANGUAGE設定程序的輸出爲zh_TW即臺灣繁體,若是terminal用來編碼的字符集是gb2312(簡體中文),則輸出會是亂碼.
前面鋪墊了那麼多,固然不會就甩出一句A!=B就結束了,說好的兩則例子呢?
咱們分別以Linux下的gnome-terminal和Windows下的Git-shell爲例演示一下上面的命令行報錯的示例
咱們先確認一下當前終端使用的編碼,在菜單Terminal->Preferences->Profiles->Edit->Compatibility->Encoding下。咱們要演示正常的和不正常的。咱們演示你們都能看懂的編碼,以英文、中文簡體、中文繁體及亂碼爲例來演示。
先查看一下terminal的編碼選擇的字符集:
ssh在傳輸過程當中可能會帶入一些變量,能夠打開verbose模式查看細節。
bash$ ssh -l root 192.168.10.85 -v debug1: Sending environment. debug1: Sending env LC_IDENTIFICATION = en_US.UTF-8 debug1: Sending env LC_TIME = en_US.UTF-8 debug1: Sending env LC_NUMERIC = en_US.UTF-8 debug1: Sending env LC_PAPER = en_US.UTF-8 debug1: Sending env LC_MEASUREMENT = en_US.UTF-8 debug1: Sending env LC_ADDRESS = en_US.UTF-8 debug1: Sending env LC_MONETARY = en_US.UTF-8 debug1: Sending env LANG = en_US.UTF-8 debug1: Sending env LC_NAME = en_US.UTF-8 debug1: Sending env LC_TELEPHONE = en_US.UTF-8 debug1: Sending env LC_CTYPE = en_US.UTF-8 Last login: Tue Feb 14 15:36:45 2017 from 192.168.23.208 Try `df --help' for more information. [root@newhis1 ~]#
當更改LANGUAGE等變量時,系統自帶的命令的輸出也會變:
[root@newhis1 ~]# LANGUAGE=zh_TW [root@newhis1 ~]# df -abcdefg df: 不適用的選項 -- b 請嘗試執行‘df --help’來獲取更多資訊。 [root@newhis1 ~]# LANGUAGE=zh_CN [root@newhis1 ~]# df -abcdefg df:無效選項 -- b 請嘗試執行「df --help」來獲取更多信息。 [root@newhis1 ~]# LANGUAGE=en_US [root@newhis1 ~]# df -abcdefg df: invalid option -- b Try `df --help' for more information. [root@newhis1 ~]#
當Terminal的字符集包含以上輸出所含的字符時,就能夠顯示正常。沒法顯示時,就會出現亂碼。
好比Terminal選的是gb2312,而LANGUAGE選擇的是zh_TW,程序輸出爲:
其中打?號的部分就是亂碼,繁體字超出了gb2312字符集所能表達的範圍,gb2312只能用來表達簡體字。
同理,若是terminal選的是UTF-8,這個字符集包含了全球可見字符,因此中文、德文都能顯示。
好比:
[root@newhis1 ~]# cat a.sh LANG=ru_UA.utf8 LANGUAGE=ru_UA LC_CTYPE=ru_UA.utf8 LC_NUMERIC=ru_UA.utf8 LC_TIME=ru_UA.utf8 LC_COLLATE="ru_UA.utf8" LC_MONETARY=ru_UA.utf8 LC_MESSAGES="ru_UA.utf8" LC_PAPER=ru_UA.utf8 LC_NAME=ru_UA.utf8 LC_ADDRESS=ru_UA.utf8 LC_TELEPHONE=ru_UA.utf8 LC_MEASUREMENT=ru_UA.utf8 LC_IDENTIFICATION=ru_UA.utf8 [root@newhis1 ~]# source a.sh
先查看當前的terminal程序全部的編碼:
由於是Windows客戶端,經過ssh登錄服務器時不會重寫LANG設置,能夠經過ssh -v選項打開調試查看登錄過程.這點與Linux上的客戶端不同.
經過如下腳本設置相關環境變量,將語言設置爲簡體中文:
#!/bin/bash lang_input=zh_CN.gb2312 LANG=$lang_input LANGUAGE=`echo $LANG|awk -F. '{print $1}'` LC_CTYPE=$lang_input LC_NUMERIC=$lang_input LC_TIME=$lang_input LC_COLLATE="$lang_input" LC_MONETARY=$lang_input LC_MESSAGES="$lang_input" LC_PAPER=$lang_input LC_NAME=$lang_input LC_ADDRESS=$lang_input LC_TELEPHONE=$lang_input LC_MEASUREMENT=$lang_input LC_IDENTIFICATION=$lang_input
測試將LANGUAGE設置爲簡體中文和繁體中文時的輸出:
LANGUAGE變量會影響到程序的輸出,當系統編碼與terminal程序使用的編碼一致的狀況下,好比都是gb2312.若是程序輸出的語言超出了編碼所選用的字符集所能表達的範圍時也會出現亂碼.若是termial程序和Linux系統選用的編碼不一致,則中文會出現亂碼.
好比,terminal選用的是ISO-8859-1(Western European),Linux系統選用的是zh_CN.GB18030
固定linux的設置不變,windows終端分別在設置爲GBK和ISO-8859-1時,咱們將這兩個截圖對比一下:
Terminal Encoding:
分別在GBK和ISO-8859-1時,在ssh上查看相同的命令輸出:
Linux System Encoding
對照一下亂碼狀況:
正常的是都選用的中文編碼的狀況,出現亂碼的是terminal選ISO-8859-1(Western European),Linux系統選zh_CN.GB18030
仍是用例子來講明,程序輸出爲繁體,但你使用的字符集不含繁體,則出現亂碼.
舉例,Linux系統選擇的是gb2312的編碼格式,terminal程序選擇的是utf8來解析.讀出來的就是亂碼.這個體如今程序輸出上就是看到的內容變成亂碼.若是把程序輸出重定向到文件,查看文件編碼類型.文件的編碼類型就是LANG格式中<語言>_<地域>.<字符集>的最後一個字段.可使用file命令或vi下:set fileencoding查看.
若是深究各類編碼,能夠查看網文:關於文件編碼格式的一點探討