0%

终端模拟器中执行命令出现乱码问题解决

一、前言

Xshell跟Gnome Terminal相比,两者都是终端模拟器(在Xshell中也可以执行简单的内置命令,如“cd”,“ls”等),地位相同。

二、原理分析

涉及到乱码,那么需要了解编码解码过程。在终端模拟器中执行命令,通信过程示意图如图1所示。

图1

在以上通信过程中,在“命令执行单元”处发生了一系列的编码解码过程,在“终端模拟器”处也发生了一系列的编码解码过程,此外,我们常常创建SSH连接,从而建立一个远端Shell会话,在该情景中,通信过程示意图如图2所示,由图2可知,在该情景中,跟原来的通信过程相比,只不过“命令执行单元”处与“终端模拟器”处之间的通信数据经过SSH安全通道而已,一般并不影响整体的编码解码过程。

图2

“命令执行单元”处执行命令过程中会发生一系列的编码解码过程,不同命令具有不同实现,因而不同命令执行过程中发生的编码解码过程也不尽相同(以下这些链接中的内容可作为该结论的证据:《使用vi和less查看文本出现中文乱码,使用cat正常》《查看日志文件more正常,用less查看出现乱码》)。另外,很多命令在执行过程中会去读取locale系列环境变量(比如“LANG”,“LANGUAGE”,“LC_ALL”,“LC_CTYPE”等)的配置值用于编码解码过程。
比如“uniq”命令会去使用“LC_COLLATE”等环境变量的配置值,“grep”命令会去使用“LC_ALL”等环境变量的配置值,“vim”命令会去使用“LC_CTYPE”等环境变量的配置值。

“终端模拟器”处执行命令过程中也会发生一系列的编码解码过程,主要有3方面内容:

  • 配置终端模拟器使用的编码方案,对于Gnome Terminal,在图3所示位置进行配置,对于Xshell,在图4所示位置进行配置,对于Xftp,在图5所示位置进行配置
  • 执行命令时,使用终端模拟器配置的编码方案,对原命令字符串进行编码,将得到的字节流传递给“命令执行单元”
  • 展现命令执行结果时,获取字节流形式的命令执行结果,使用终端模拟器配置的编码方案,对其进行解码,得到字符串形式的命令执行结果

图3

图4

图5

三、实验

3.1、ls命令

3.1.1、实验1

现有一个名为“你好吗.txt”的文件,相应于该文件名的存储内容存储的是使用“UTF-8”编码方案编码得到的字节流。
设置不同的locale系列环境变量配置值和终端模拟器编码方案,得到不同的“ls命令”执行结果,具体如表1。

表1

locale系列环境变量配置值 终端模拟器(实验了Gnome Terminal和Xshell)编码方案 ls命令执行结果截图
zh_CN.utf8 UTF-8 这里写图片描述
zh_CN.gbk UTF-8 这里写图片描述
zh_CN.utf8 GBK 这里写图片描述
zh_CN.gbk GBK 这里写图片描述

3.1.2、实验2

现有一个名为“你好吗.txt”的文件,相应于该文件名的存储内容存储的是使用“GBK”编码方案编码得到的字节流。
设置不同的locale系列环境变量配置值和终端模拟器编码方案,得到不同的“ls命令”执行结果,具体如表2。

表2

locale系列环境变量配置值 终端模拟器(实验了Gnome Terminal和Xshell)编码方案 ls命令执行结果截图
zh_CN.utf8 UTF-8 这里写图片描述
zh_CN.gbk UTF-8 这里写图片描述
zh_CN.utf8 GBK 这里写图片描述
zh_CN.gbk GBK 这里写图片描述

3.2、vim命令

3.2.1、实验1

现有一个名为“a.txt”的文件,存储有内容“你好吗?”,使用的编码格式为“UTF-8”。
设置不同的locale系列环境变量配置值和终端模拟器编码方案,得到不同的“vim a.txt”执行结果,具体如表3。

表3

locale系列环境变量配置值(跟“3.1、ls命令”一样,也是将locale系列中的所有环境变量的配置值设为同一个值) 终端模拟器(实验了Gnome Terminal和Xshell)编码方案 “vim a.txt”命令执行结果截图 VIM环境中执行“:e ++enc=utf8”结果截图
zh_CN.utf8 UTF-8 这里写图片描述 这里写图片描述
zh_CN.gbk UTF-8 这里写图片描述 这里写图片描述
zh_CN.utf8 GBK 这里写图片描述 这里写图片描述
zh_CN.gbk GBK 这里写图片描述 这里写图片描述

3.2.2、实验2

现有一个名为“a.txt”的文件,存储有内容“你好吗?”,使用的编码格式为“GBK”。
设置不同的locale系列环境变量配置值和终端模拟器编码方案,得到不同的“vim a.txt”执行结果,具体如表4。

表4

locale系列环境变量配置值(跟“3.1、ls命令”一样,也是将locale系列中的所有环境变量的配置值设为同一个值) 终端模拟器(实验了Gnome Terminal和Xshell)编码方案 “vim a.txt”命令执行结果截图 VIM环境中执行“:e ++enc=gbk”结果截图
zh_CN.utf8 UTF-8 这里写图片描述 这里写图片描述
zh_CN.gbk UTF-8 这里写图片描述 这里写图片描述
zh_CN.utf8 GBK 这里写图片描述 这里写图片描述
zh_CN.gbk GBK 这里写图片描述 这里写图片描述

四、其他

4.1、locale系列环境变量

在以上实验中,locale系列中的所有环境变量的配置值都被设为“zh_CN.gbk”或者“zh_CN.utf8”,其实“ls命令实现”或者“vim命令实现”只需要读取locale系列中特定的部分的环境变量的配置值即可,locale系列中其他的环境变量的配置值并不会影响“ls命令”或者“vim命令”的运行。只不过我们并不知道上述提及的“locale系列中特定的部分的环境变量”是哪些,故而为了简单起见,将locale系列中的所有环境变量的配置值都设为“zh_CN.gbk”或者“zh_CN.utf8”。

4.2、推测vim命令读取文件内容发生的主要的编码解码过程

由“3.2、vim命令”中的实验可推知,使用vim命令读取文件内容中发生的主要的编码解码过程如下:读取文件内容的字节流,使用locale系列环境变量指定的编码方案(也可以通过“:e ++enc=?”命令自己指定要使用的编码方案)对字节流进行解码得到文件内容的字符流,再使用locale系列环境变量指定的编码方案对获得的字符流进行编码得到字节流,将前述编码得到的字节流传递给终端模拟器。
因而,只要locale系列环境变量指定的编码方案跟终端模拟器使用的编码方案一致,那么直接可以得到文件内容的非乱码展现或者通过“:e ++enc=?”命令得到文件内容的非乱码展现。

4.3、实验中用到的两个脚本

脚本1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/bin/bash 

cd /home/dsl/tmp/shell

export LC_ALL=$1
export LANG=$1
export LANGUAGE=$1

locale

echo "---------"
echo "---------"
echo "---------"

ls

脚本2:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/bin/bash 

cd /home/dsl/tmp/shell

export LC_ALL=$1
export LANG=$1
export LANGUAGE=$1

locale

echo "---------"
echo "---------"
echo "---------"

vim a.txt
您的支持将鼓励我继续分享!