相信很多人(也可能只有我啦)使用樹莓派或新安裝 Linux 的時候,ssh 進 Server 後會遇到這個問題:

1
2
3
4
5
6
7
8
perl: warning: Setting locale failed.
perl: warning: Please check that your locale settings:
        LANGUAGE = (unset),
        LC_ALL = (unset),
        LC_CTYPE = "UTF-8",
        LANG = "en_US.UTF-8"
    are supported and installed on your system.
perl: warning: Falling back to a fallback locale ("en_US.UTF-8").

不只是 perl 出了問題(會用 perl 當例子只是因為他最好觸發),只要是 apt update 或是 git clone 的時候都會跳出這個問題,就是:

你沒設定好你的語系

同時這個問題困擾我很久了,因此趁時機來一次解決這個問題。

本篇就是來闡述發生了什麼事情。

Locale Setting

首先,第一種可能就是,語系檔沒安裝。如果有設定 LANG = "en_US.UTF-8" ,結果系統裡沒有該文檔,那當然會出現問題囉!

也就是明明全域變數中 LANG = "en_US.UTF-8",結果系統中沒有這個語系檔。

這種時候就是確認 /etc/locale.gen 中,有沒有把該語系 uncomment 掉,並且執行一次 locale-gen 來產生該語系檔案。或是 dpkg-reconfigure locales 來自動設定 locale 設定。如果是樹莓派的話,更簡單,執行 raspi-config ,並在選單中直接調整語系就行了。

最後重新啟動 shell,隨便執行一個需要語系的指令,確認是否解決問題。

以上指令都需要 sudo 來執行。

LC_CTYPE

但問題其實來自於這個東西:LC_CTYPE

最一開始的 Warning Log 中其實就顯示了:

1
2
LC_CTYPE = "UTF-8",
LANG = "en_US.UTF-8"

至於為什麼會出問題,很明顯的,LC_CTYPELANG 不匹配,又或是根本沒有語系與 LC_CTYPE 相符。

這點可以直接在 bash 中輸入 unset LC_CTYPE 試試看,將將,問題解決了。

所以一切的元兇就是這個變數,那這個變數怎麼來的呢?

只有 MacOS 的人才有這個問題,Windows 使用者可能完全沒感覺。

因為 MacOS 中,/etc/ssh/ssh_config 有這一行。

1
2
Host *
    SendEnv LANG LC_*

代表說「在 ssh 後,傳入這些變數進 Server。」

於是輸入 echo $LC_CTYPE 後,發現 MacOS 的 LC_CTYPE 果然長得跟 Server 裡的一模一樣,到這裡已經可以有頭緒該怎麼解決了。

怎麼解決?

這邊提供幾種思路。

Server Shell Profile 解決法

總之為何不要在 Server 中的 .profile 或是,.zprofile (zsh) 中明確地宣告呢?這樣就能直接確保在登入 Server Shell 時,這個設定會被覆寫。

1
unset LC_CTYPE

頭痛醫頭法

先 unset 本地的變數,然後再 ssh 進去,並且由於括號的特性(括號內變動會在離開括號時復原),執行完之後不會影響到 Local 的變數。<Server> 請帶入自己的 ip 跟 username。

1
(unset LC_CTYPE ;ssh <Server>)

修改 ssh config

這裡有兩種方法,分別是設定 Client 或是設定 Server。兩者擇一即可。

在自己的電腦上,也就是 Client 中,設定 /etc/ssh/ssh_config。找到以下內容,並將該句註解掉。

1
2
3
# Client
# comment out / remove the following line
SendEnv LANG LC_*

或是在伺服器端中設定 /etc/ssh/sshd_config

1
2
3
# Server
# comment out / remove the following line
AcceptEnv LANG LC_*

結論

總之這次的問題來源在 MacOS 亂傳遞了變數。雖然不會影響使用,但就是看了很煩。

我會推薦修改自己電腦裡的 /etc/ssh/ssh_config。因為大部分情況下其實用不到語系傳遞,還不如直接解決問題根源。

希望有幫助到有同樣問題的人。

Appendix. Locale Env

最後列出,LC 的家族們以及它的用途。

  1. LC_CTYPE: 字元分類及處理方式。
  2. LC_COLLATE: 字元順序與字串比較。
  3. LC_MESSAGES: 程式中用何種語言來顯示訊息。
  4. LC_MONETARY: 貨幣顯式格式。
  5. LC_NUMERIC: 數字顯式格式。
  6. LC_TIME: 日期與時間的顯式格式。
  7. LC_ALL: 此類別可以一次設定以上所有的類別。
  8. LANG: 作用類似 LC_ALL,也可用來一次設定所有的 locale 環境。

Reference

  1. https://stackoverflow.com/questions/29609371/how-not-to-pass-the-locale-through-an-ssh-connection-command
  2. https://www.csie.ntu.edu.tw/~r95053/samples/collection/backup/locale2.html