最近项目上需要SSH远程到Centos7容器上执行Python代码,Python版本为3.6。在执行打开中文名称的文件时,在Centos7容器上本地执行open('狼来了.txt')
不会有任何问题,但当通过SSH连接过去后发现open()方法报如下错误:
UnicodeEncodeError :'ascii' codec can't encode characters in position 0-2
问题出现在open的时候将 '狼来了.txt'(中文文件名称)作为参数按照ascii编码方式进行encode,而我们知道ascii编码是只有128位包含数字、大小写字母和一些特殊符号,是不包含中文汉字的。【至于为什么会进行encode,本文不做介绍】
所以将代码做如下更改便可以正常运行:
open('狼来了.txt'.encode('utf8'))
因为这里我们指定了了utf-8
的编码格式(当然,需要运行环境支持)。
找到问题的本质,我们就可以接着来看SSH的问题。
Python运行环境容器里通过locale
以及locale -a
命令来查看发现语言编码环境如下所示:
[root@pyrun-test-69d4d45d79-mqg6n /]# locale -a C en_US.utf8 POSIX [root@pyrun-test-69d4d45d79-mqg6n /]# locale LANG=en_US.utf8 LC_CTYPE="en_US.utf8" LC_NUMERIC="en_US.utf8" LC_TIME="en_US.utf8" LC_COLLATE="en_US.utf8" LC_MONETARY="en_US.utf8" LC_MESSAGES="en_US.utf8" LC_PAPER="en_US.utf8" LC_NAME="en_US.utf8" LC_ADDRESS="en_US.utf8" LC_TELEPHONE="en_US.utf8" LC_MEASUREMENT="en_US.utf8" LC_IDENTIFICATION="en_US.utf8" LC_ALL=
而SSH客户端的语言编码环境如下:
root@coding-editor-test-8c6cdfdd8-9tpcl:/app# locale -a C C.UTF-8 POSIX root@coding-editor-test-8c6cdfdd8-9tpcl:/app# locale LANG=C.UTF-8 LANGUAGE= LC_CTYPE="C.UTF-8" LC_NUMERIC="C.UTF-8" LC_TIME="C.UTF-8" LC_COLLATE="C.UTF-8" LC_MONETARY="C.UTF-8" LC_MESSAGES="C.UTF-8" LC_PAPER="C.UTF-8" LC_NAME="C.UTF-8" LC_ADDRESS="C.UTF-8" LC_TELEPHONE="C.UTF-8" LC_MEASUREMENT="C.UTF-8" LC_IDENTIFICATION="C.UTF-8" LC_ALL=
通过对比发现SSH客户端的编码环境是C.UTF-8
,而Python运行环境为en_US.utf8
(ps : C表示的是ascii编码, en_US.utf8 与 zh_CN.utf8 都是包含汉字的)
首先想到的是可能是SSH客户端影响了Python运行时的默认encode方式,那么我们把SSH客户端的环境设置成和Python运行环境一样的『这里客户端为Ubuntu』:
#!/bin/sh # 1.设置语言为en_US_UTF-8 echo -e 'LANG="en_US_UTF-8"\nLANGUAGE="en_US:en"' >> /etc/default/locale # 生效配置 source /etc/default/locale # 2.如果缺少en_US_UTF-8语言包 安装locales工具并设置en_US_UTF-8 apt-get install --no-install-recommends -y locales locale-gen en_US.UTF-8 localedef -v -c -i en_US -f UTF-8 en_US.UTF-8
设置完成后再尝试SSH到Python运行环境容器,发现open('狼来了.txt')
运行不再报错。
那么问题来了,为什么SSH会用客户端的编码运行环境
cat /etc/ssh/ssh_config
在ssh的配置文件中发现如下内容:
上面配置,会将本地的语言环境SendEnv到Python运行环境,那么我们就不需要保证客户端和Python运行环境的语言环境一致了。
只需要将配置修改为:
SendEnv LANG en_US.utf8
由于项目是由Java通过Mina SSH 调用Python运行环境终端去执行的代码,而非直接用的SSH,因此对/etc/ssh/ssh_config
的修改并不能解决问题。
通过对Mina的观察发现,再创建ChannelShell的时候可以传递参数,源码接口如下:
/** * Create a channel to start a shell using specific PTY settings and/or environment. * * @param ptyConfig The PTY configuration to use - if {@code null} then internal defaults are used * @param env Extra environment configuration to be transmitted to the server - ignored if * {@code null}/empty. * @return The created {@link ChannelShell} * @throws IOException If failed to create the requested channel */ ChannelShell createShellChannel( PtyChannelConfigurationHolder ptyConfig, Map<String, ?> env) throws IOException;
那么在创建ChannelShell的时候可以这样写,将语言编码配置:
shellChannel = session.createShellChannel(new PtyChannelConfiguration(), Map.of("LANG","en_US.utf8"));