本地变量在远程shell中没有任何效果(perl: 警告:设置区域失败。)

我有一个全新的Ubuntu 12.04安装。当我连接到我的远程服务器时,出现了这样的错误:
~$ ssh example.com sudo aptitude upgrade
...
Traceback (most recent call last):
  File "/usr/bin/apt-listchanges", line 33, in <module>
    from ALChacks import *
  File "/usr/share/apt-listchanges/ALChacks.py", line 32, in <module>
    sys.stderr.write(_("Can't set locale; make sure $LC_* and $LANG are correct!\n"))
NameError: name '_' is not defined
perl: warning: Setting locale failed.
perl: warning: Please check that your locale settings:
    LANGUAGE = (unset),
    LC_ALL = (unset),
    LC_TIME = "de_DE.UTF-8",
    LC_MONETARY = "de_DE.UTF-8",
    LC_ADDRESS = "de_DE.UTF-8",
    LC_TELEPHONE = "de_DE.UTF-8",
    LC_NAME = "de_DE.UTF-8",
    LC_MEASUREMENT = "de_DE.UTF-8",
    LC_IDENTIFICATION = "de_DE.UTF-8",
    LC_NUMERIC = "de_DE.UTF-8",
    LC_PAPER = "de_DE.UTF-8",
    LANG = "en_US.UTF-8"
    are supported and installed on your system.
perl: warning: Falling back to the standard locale ("C").
locale: Cannot set LC_ALL to default locale: No such file or directory
No packages will be installed, upgraded, or removed.
0 packages upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
Need to get 0 B of archives. After unpacking 0 B will be used.
...

我在连接旧版Ubuntu时没有这个问题。 这是我Ubuntu 12.04安装的输出,LANG和LANGUAGE已设置。
$ locale
LANG=de_DE.UTF-8
LANGUAGE=de_DE:en_GB:en
LC_CTYPE="de_DE.UTF-8"
LC_NUMERIC=de_DE.UTF-8
LC_TIME=de_DE.UTF-8
LC_COLLATE="de_DE.UTF-8"
LC_MONETARY=de_DE.UTF-8
LC_MESSAGES="de_DE.UTF-8"
LC_PAPER=de_DE.UTF-8
LC_NAME=de_DE.UTF-8
LC_ADDRESS=de_DE.UTF-8
LC_TELEPHONE=de_DE.UTF-8
LC_MEASUREMENT=de_DE.UTF-8
LC_IDENTIFICATION=de_DE.UTF-8
LC_ALL=

有人知道在Ubuntu上发生了什么变化,以致于在远程服务器上出现这个错误信息吗?

1可能是如何解决我的区域设置问题?的重复问题。 - Fabby
7个回答

这是因为您本地机器的区域设置为德语,SSH会将其转发并尝试在服务器上使用,但您的服务器没有安装该语言包。
您有几个选择:
  • 生成本地化设置。在服务器上生成德语本地化设置,使用sudo locale-gen de命令。

  • 停止从客户端转发本地化设置。不要将本地机器的本地化环境变量转发到服务器。您可以在本地 /etc/ssh/ssh_config文件中注释掉SendEnv LANG LC_*行。

  • 停止在服务器上接受本地化设置。不要接受本地机器的本地化环境变量到服务器。您可以在远程 /etc/ssh/sshd_config文件中注释掉AcceptEnv LANG LC_*行。

  • 将服务器本地化设置为英语。在服务器上明确设置本地化为英语。例如,您可以在远程的~/.bashrc~/.profile文件中添加以下行:

    export LANGUAGE="en"
    export LANG="C"
    export LC_MESSAGES="C"
    
如果您没有对服务器的根访问权限,那么“停止从客户端转发区域设置”选项可能是最好(也是唯一)的选择。

3哈!我完全错过了他列出的客户端区域设置!干得好... - ish
13我决定“停止从客户端中转区域设置”。效果不错。供以后参考:您无法在本地的~/.ssh/config中覆盖/etc/ssh/ssh_config中的SendEnv设置。详见https://bugzilla.mindrot.org/show_bug.cgi?id=1285 - Janning
4结合https://bugs.php.net/bug.php?id=18556,SSH发送的本地化信息可能会引起实际问题(并且对我来说确实造成了问题),请参阅https://bugzilla.mindrot.org/show_bug.cgi?id=1285#c9... - Halil Özgür
1在我的情况下,将区域设置设置在~/.profile文件中解决了我的问题。谢谢。 - Francisco
如果服务器没有德语语言环境,为什么它可以用其他值覆盖用户请求的LANG值而被接受呢? - Mikhail T.
如果系统中没有可用的区域设置,您将无法生成该区域设置。要使其可用,首先需要运行 sudo dpkg-reconfigure locales 命令。请参阅Debian区域设置维基页面 - Paul Rougieux
停止从客户端转发区域设置,这让我免于疯狂,谢谢。 - Setop

有时候在新的最小/备选安装或其他情况下会发生这种情况。修复方法非常简单。按以下顺序尝试这些方法,并在每次尝试后进行测试以查看是否已解决问题:
1. 重新配置本地设置
- sudo dpkg-reconfigure locales - 如果这行不通,
2. 重新安装语言包
- sudo apt-get --reinstall install language-pack-de - 如果这行不通,
3. 手动强制设置区域设置(持久性)
- sudo update-locale LC_ALL=de_DE.UTF-8 LANG=de_DE.UTF-8

我有一个JVM的问题,我也在尝试在12.04中设置系统区域设置。唯一的问题是,无论我尝试什么,似乎都无法设置LC_ALL,locales仍然显示为空。 - Dark Star1

/etc/ssh/ssh_config中注释掉SendEnv LANG LC_*这一行,使其看起来像这样:
#SendEnv LANG LC_*

问题

默认情况下,ssh客户端命令会将与区域设置相关的环境变量转发到SSH服务器。这在客户端的/etc/ssh/ssh_config中指定:

Host *
    SendEnv LANG LC_*

默认情况下,SSH服务器恰好接受它们(在服务器上的/etc/ssh/sshd_config文件中):
AcceptEnv LANG LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES
AcceptEnv LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT
AcceptEnv LC_IDENTIFICATION LC_ALL LANGUAGE

所以,如果您的shell中有与地区相关的环境变量,它们将被传递到服务器端的SSH会话中。
不幸的是,SendEnv选项是累积的。根据man 5 ssh_config的说明:
 SendEnv
         ... Multiple environment variables may be separated
         by whitespace or spread across multiple SendEnv directives.  The
         default is not to send any environment variables.

这意味着它无法被覆盖。
解决方案
有时候改变整个系统的配置是不可能或不明智的,特别是在服务器端。然而,你可以绕过它。这就是ssh命令中-F选项的副作用。根据man ssh的说明:
 -F configfile
         Specifies an alternative per-user configuration file.  If a con-
         figuration file is given on the command line, the system-wide
         configuration file (/etc/ssh/ssh_config) will be ignored.  The
         default for the per-user configuration file is ~/.ssh/config.

默认情况下,如果存在用户配置文件~/.ssh/config,则会使用它。但是,您可以在命令行上明确指定以绕过/etc/ssh/ssh_config

$ touch ~/.ssh/config
$ ssh -F ~/.ssh/config your_user@your_host

如果你在~/.bashrc中创建一个别名,会更方便。

alias ssh="ssh -F ~/.ssh/config"

通过这种方式,系统范围的配置中默认的SendEnv指令将不起作用,默认情况下不会发送任何环境变量到SSH服务器。

3请记住,在/etc/ssh/config中的系统配置文件可能包含您希望保留在用户配置中的其他行。您需要将它们复制到~/.ssh/config中的用户配置文件中,以保留这些设置。 - Steven Maude

我遇到了类似的问题。我的解决方法是在`/etc/ssh/ssh_config`中注释掉`SendEnv`行(因为这些不能被覆盖),并在`~/.ssh/config`中添加以下条目:
Host *,!<somehost>
    SendEnv LANG LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES
    SendEnv LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT
    SendEnv LC_IDENTIFICATION LC_ALL LANGUAGE
    SendEnv XMODIFIERS    

使用<somehost>作为主机名,我不想发送任何环境变量。


这个问题在OpenSSH 7.8中已经修复,即早于Debian稳定版的当前版本。文档现在包括以下内容:
可以通过在模式前加上-来清除先前设置的SendEnv变量名称。默认情况下不发送任何环境变量。 参见#573316

我遇到了一个问题,我认为是由于ssh传递了区域设置,所以我尝试在客户端禁用它,然后在服务器端也禁用它,但登录后仍然出现了区域设置的问题。
结果发现目标服务器本身的区域设置混乱,设置是从/etc/default/locale中获取的。
我不得不彻底清理它,并运行sudo dpkg-reconfigure locales来解决这个问题。

谢谢,这解决了我的问题。我必须添加sudo以便在su下工作。https://serverfault.com/a/992110/962047 - AKMalkadi