乱码是字节与解释方式不匹配
远端执行命令后输出乱码,本质不是数据错了,而是同一段字节被用错了编码去解释。链路上至少有三个环节各自持有编码假设:远程程序的输出编码、传输过程中是否被转换、以及本地终端或日志系统的解析编码。只要其中一环与实际字节不一致,就会显示为乱码。排查的入手点也就很明确——先确定这段字节真实是什么编码,再检查谁在用另一种编码解释它。
基本信息
| 适用对象 | Linux 与 Windows 主机的终端输出、日志、脚本执行 |
|---|---|
| 关键变量 | 语言与区域设置相关环境变量,决定默认编码 |
| 常见错配 | 远端按本地编码输出、接收端按统一编码解析 |
| 检测手段 | 文件编码探测工具与十六进制查看 |
| 权限要求 | 查看与修改系统级语言设置需 root |
典型错配场景
- 远端输出为本地编码:中文环境下默认输出不是统一编码时,统一编码终端会显示乱码
- 环境变量未传递:登录后远端默认编码与服务端配置不符,脚本输出随之变化
- 日志采集器假定编码:采集过程按统一编码解析,遇到其他编码就出现替换字符
- 换行符差异:跨平台脚本文本中的回车符会污染输出,表现为每行末尾多余符号
- 数据库连接字符集:客户端与服务端字符集不一致时,写入的中文变成问号或乱码
- 命令输出重定向:非交互执行时编码假设与交互终端不同,同一命令结果不一致
定位与转换
定位与转换
locale # 当前会话的语言与编码设置
locale -a | grep -i utf # 系统已生成哪些区域设置
file -i report.csv # 探测文件声明的编码
ticonv -f gb18030 -t utf-8 old.csv > new.csv # 明确源编码后再转换
iconv -f gb18030 -t utf-8 -c old.csv > new.csv # 无法转换的字符直接丢弃
od -c out.txt | head -5 # 看真实字节,确认是否有回车符
LANG=C.UTF-8 command 2>&1 # 单次执行时固定编码
PYTHONIOENCODING=utf-8 python3 script.py # 脚本输出编码单独指定表现与推断
| 表现 | 可能的编码关系 | 下一步 |
|---|---|---|
| 中文变问号 | 写入侧不支持该字符 | 统一为支持多字节的编码 |
| 中文变方块或问号菱形 | 解析侧遇到无法解释的字节 | 探测真实编码后重新解释 |
| 每行末尾多一个符号 | 换行符跨平台差异 | 转换行结束符或改用无转换模式 |
| 参数能过但记录出错 | 客户端与服务端字符集不一致 | 核对连接字符集与表定义 |
| 同一命令结果不同 | 交互与非交互的编码假设不同 | 固定执行环境的编码 |
转换的前提是知道源编码。用错误的源编码做转换会得到另一堆乱码,甚至造成不可逆的字符丢失,因此应先探测或回溯数据的产生方式,再动手转换;无法确定编码时保留原始文件不动。
跨平台自动化里,统一使用不带本地化假设的编码最省事:脚本文件本身、执行环境变量与日志采集解析三处保持一致,可以避免绝大多数间歇性乱码。