快速修复
# 一键(在 PowerShell 脚本中执行):设置官方 UTF-8 开关,wsl 输出即恢复正常
$env:WSL_UTF8 = "1"
放在脚本开头;或临时 cmd /c "set WSL_UTF8=1 && wsl ..."。
你会遇到什么
PowerShell(5.1 和 7)里调用 wsl.exe ... 时输出乱码:w s l : ... 每个字符中间有空格(UTF-16LE 的 00 字节被当字符显示)、wsl --help | more 每字符一行(microsoft/WSL#13093 官方 issue)、中文内容变成 �。
直接 wsl echo hi > 文件(cmd 重定向)字节正常;坏的是管道/捕获场景:PowerShell 脚本赋值、CI 抓取、| 管道。
本文路径:快速修复(30 秒一段命令)→ 一步步排查(可跳读)→ 原理与变体(症状/原因/证据)。赶时间直接跳到「快速修复」。
为什么会这样
wsl.exe 在管道/非 TTY 场景输出 UTF-16LE(无 BOM),PowerShell 捕获后按自己的 stdout 编码(实测 PS 5.1 与 pwsh 7 的 -Command 链路都是 UTF-16LE 无 BOM)写到下游;下游按 UTF-8/单字节解码 → 每个 ASCII 字符被 00 隔开,中文全变 �。
这是 wsl.exe 的已知行为,2025 年的 #13093(WSL 2.5.7)仍存在。
wsl.exe 是 Windows 程序,它决定输出编码的依据是输出目的地:连到终端时按 UTF-8,连到管道/非 TTY 时按 Windows 原生宽字符 UTF-16LE(无 BOM)输出——这是 wsl.exe 的既定行为,不是 bash 决定的。
PowerShell 捕获后按自己的 stdout 编码继续传递,下游程序(按 UTF-8/单字节假设)解码时把每个 ASCII 字节后面的 00 也当字符,于是「中文字符变 �、ASCII 被 00 隔开」。
确定性推论:管道场景不要依赖 wsl 输出做文本解析,两条正路——wsl.exe -e bash -lc "..." 让 bash 层接管输出编码,或在 PowerShell 里 [Console]::OutputEncoding=[Text.Encoding]::UTF8 显式声明;
写文件用 wsl bash -lc 'cmd > file' 让重定向发生在 Linux 侧,绕开 wsl.exe 的编码判定。
一步步来
1. 设 $env:WSL_UTF8 = "1" — 必做
微软官方开关,实测 pwsh -File 下 wsl 输出变 UTF-8 正常;不想改全局就放在脚本开头。
2. 复杂/长命令别硬拼命令行 — 必做
转义、长度都会炸。把脚本写到 WSL 内文件再执行:
wsl bash /tmp/x.sh
PowerShell 写文件走 UNC:Set-Content \\wsl.localhost\Ubuntu\tmp\x.sh。
3. 超长/特殊字符用 base64 传输 — 必做
wsl bash -lc "echo <base64> | base64 -d | bash"
实测通过,绕开引号/转义/长度三坑。
4. 输出走文件 — 必做
让 wsl 内命令把结果写 /tmp/out.txt,再用 Get-Content \\wsl.localhost\Ubuntu\tmp\out.txt 读——UTF-8 文件直读无编码问题,比管道可靠。
5. 辅助手段 — 必做
[Console]::OutputEncoding = [Text.Encoding]::UTF8 影响 PowerShell 写控制台编码,可配合 1 使用。WSL 启动横幅("检测到 localhost 代理配置...")是 stderr 噪声:设了 WSL_UTF8 后不乱码,仍介意可忽略或加 2>$null。
怎么确认修好了
字节级验证(实测输出形态):
ps1 里 $env:WSL_UTF8="1" 后 & wsl.exe echo hi → 字节 77 73 6C 3A 20 E6 A3 80(UTF-8 正常,中文不丢)
复现基线:pwsh -NoProfile -Command "wsl echo hi" → 字节 77 00 73 00 6C 00 ...(UTF-16LE 无 BOM,实测)。
其它环境的表现
| 环境 | 说明 | |
|---|---|---|
| cmd 重定向 | 字节正常(UTF-8,实测对照) | |
| PowerShell 管道/捕获 | UTF-16LE 无 BOM(PS 5.1 与 pwsh 7 均复现,#13093 同款) | |
| base64 通道 | `wsl bash -lc "echo <b64> | base64 -d"` 实测通过 |
| 文件中转 | /tmp/out.txt + UNC 读取,无编码问题(天天在用) | |
| stderr 横幅 | WSL_UTF8 后不乱码,介意加 2>$null |
症状
PowerShell(5.1 和 7)里调用 wsl.exe ... 时输出乱码:
w s l : �hKm0R ... # 每个字符中间有空格(UTF-16LE 的 00 字节被当字符显示)
wsl --help | more # 每字符一行(microsoft/WSL#13093 官方 issue)
中文内容变成 � 一类替换符
特征:直接 wsl echo hi > 文件(cmd 重定向)字节正常(UTF-8); 坏的是管道/捕获场景:PowerShell 脚本赋值、CI 抓取、| 管道; WSL 启动时还会混入一段代理检测横幅(stderr),加剧观感。
原因
wsl.exe 在管道/非 TTY 场景输出 UTF-16LE(无 BOM),PowerShell 捕获后按自己的 stdout 编码 (实测 PS 5.1 与 pwsh 7 的 -Command 链路都是 UTF-16LE 无 BOM)写到下游; 下游按 UTF-8/单字节解码 → 每个 ASCII 字符被 00 隔开,中文全变 �。 这是 wsl.exe 的已知行为,2025 年的 #13093(WSL 2.5.7)仍存在。
解法
- 脚本里设
$env:WSL_UTF8 = "1"再调 wsl(微软官方开关,实测 pwsh -File 下 wsl 输出变 UTF-8 正常);
不想改全局就放在脚本开头,或临时 cmd /c "set WSL_UTF8=1 && wsl ..."。
- 复杂/长命令别硬拼命令行(转义、长度都会炸):把脚本写到 WSL 内文件再执行:
wsl bash /tmp/x.sh;PowerShell 写文件走 UNC:Set-Content \\wsl.localhost\Ubuntu\tmp\x.sh。
- 超长/特殊字符用 base64 传输:
wsl bash -lc "echo <base64> | base64 -d | bash"(实测通过,绕开引号/转义/长度三坑)。 - 输出走文件:让 wsl 内命令把结果写
/tmp/out.txt,再用Get-Content \\wsl.localhost\Ubuntu\tmp\out.txt读——
UTF-8 文件直读无编码问题,比管道可靠。
- 辅助手段:
[Console]::OutputEncoding = [Text.Encoding]::UTF8影响 PowerShell 写控制台编码,可配合 1 使用。 - WSL 启动横幅("检测到 localhost 代理配置...")是 stderr 噪声:设了 WSL_UTF8 后不乱码,仍介意可忽略或加
2>$null。
验证证据
- 本地实测(2026-08-29,Windows 11 + WSL Ubuntu,PowerShell 5.1 与 pwsh 7):
- 对照:cmd 直调
wsl.exe echo hi > 文件→ 字节68 69 0A(UTF-8,正常); - 复现:
pwsh -NoProfile -Command "wsl echo hi" > 文件→ 字节77 00 73 00 6C 00 ...(UTF-16LE 无 BOM),
下游按 UTF-8 显示即 "w s l :" 乱码——与本条目开头截图同款,#13093 同款;PowerShell 5.1 同样复现;
- 修复:ps1 里
$env:WSL_UTF8="1"后& wsl.exe echo hi→ 字节77 73 6C 3A 20 E6 A3 80(UTF-8 正常,中文不丢); - base64 通道:
wsl bash -lc "echo dGVzdA== | base64 -d"→ test(exit 0); - 文件中转:WSL 内写 /tmp/out.txt 后 UNC 读取,无编码问题(实践天天在用)。
- 实测复现日志(2026-08-29)
适用工具 (1)
本问题方案已挂到下列工具的方案页——从工具页也能回到这里,双向可达。
wsl · 外部工具来源与证据
← 编码代理与终端 问题域 | 问题货架 | 遇到其它问题?去 问题空间 提交求助卡