Quick fix
# One-liner (inside a PowerShell script): set the official UTF-8 switch, wsl output returns to normal
$env:WSL_UTF8 = "1"
Put it at the script start; or temporarily cmd /c "set WSL_UTF8=1 && wsl ...".
What you will run into
Calling wsl.exe ... from PowerShell (5.1 and 7) garbles the output: w s l : ... with a space between every character (UTF-16LE 00 bytes shown as characters), wsl --help | more prints one character per line (official issue microsoft/WSL#13093), Chinese content becomes �.
Direct wsl echo hi > file (cmd redirection) bytes are fine; the broken cases are pipe/capture scenarios: PowerShell script assignment, CI capture, | pipelines.
Why this happens
In pipe/non-TTY scenarios wsl.exe outputs UTF-16LE (no BOM); PowerShell captures it and writes downstream per its own stdout encoding (measured: both PS 5.1 and pwsh 7 -Command chains get UTF-16LE no BOM);
downstream decodes as UTF-8/single-byte → every ASCII char separated by 00, Chinese all �.
This is wsl.exe's known behavior, still present in 2025's #13093 (WSL 2.5.7).
wsl.exe is a Windows program, and it decides output encoding by where the output goes: connected to a terminal it emits UTF-8;
connected to a pipe/non-TTY it emits Windows-native wide characters, UTF-16LE without BOM — this is wsl.exe's defined behavior, not bash's choice.
PowerShell captures it and forwards it using its own stdout encoding, and downstream programs that assume UTF-8/single-byte decode every ASCII byte followed by a 00 as a character — Chinese becomes mojibake and ASCII gets padded with nulls.
Deterministic takeaway: never rely on wsl.exe output for text parsing in pipeline scenarios; two correct paths — wsl.exe -e bash -lc "..." so the bash layer controls output encoding, or declare [Console]::OutputEncoding=[Text.Encoding]::UTF8 in PowerShell;
for file output, redirect inside Linux (wsl bash -lc 'cmd > file') to bypass wsl.exe's encoding decision entirely.
Step by step
1. Set $env:WSL_UTF8 = "1" — Required
Official Microsoft switch; measured: under pwsh -File, wsl output becomes normal UTF-8; if you don't want global change, put it at the script start.
2. Don't hand-build complex/long command lines — Required
Escaping and length both blow up. Write the script to a file inside WSL and execute it:
wsl bash /tmp/x.sh
PowerShell writes files via UNC: Set-Content \\wsl.localhost\Ubuntu\tmp\x.sh.
3. Use base64 transport for very long/special-char content — Required
wsl bash -lc "echo <base64> | base64 -d | bash"
Measured working; dodges the quoting/escaping/length pitfalls.
4. Route output through files — Required
Have the in-WSL command write results to /tmp/out.txt, then read with Get-Content \\wsl.localhost\Ubuntu\tmp\out.txt — UTF-8 files read directly with no encoding issues, more reliable than pipes.
5. Auxiliary measures — Required
[Console]::OutputEncoding = [Text.Encoding]::UTF8 affects PowerShell's console-output encoding; can combine with #1. The WSL startup banner ("Detected localhost proxy configuration...
") is stderr noise: with WSL_UTF8 set it's not garbled; if you mind it, ignore it or add 2>$null.
How to confirm it works
Byte-level verification (measured output shape):
After $env:WSL_UTF8="1" in a ps1, & wsl.exe echo hi → bytes 77 73 6C 3A 20 E6 A3 80 (normal UTF-8, Chinese intact)
Reproduction baseline: pwsh -NoProfile -Command "wsl echo hi" → bytes 77 00 73 00 6C 00 ... (UTF-16LE no BOM, measured).
Other environments
| Environment | Notes | |
|---|---|---|
| cmd redirection | Bytes fine (UTF-8, measured control) | |
| PowerShell pipe/capture | UTF-16LE no BOM (reproduced on both PS 5.1 and pwsh 7; same as #13093) | |
| base64 channel | `wsl bash -lc "echo <b64> | base64 -d"` measured working |
| File relay | /tmp/out.txt + UNC read; no encoding issues (used daily) | |
| stderr banner | Not garbled after WSL_UTF8; add 2>$null if it bothers you |
Symptom
Calling wsl.exe ... from PowerShell (5.1 and 7) produces garbled output:
w s l : �hKm0R ... # spaces between every character (UTF-16LE 00 bytes rendered as characters)
wsl --help | more # one character per line (microsoft/WSL#13093 official issue)
Chinese content becomes � replacement chars
Key traits: direct wsl echo hi > file (cmd redirection) is fine (UTF-8 bytes); the damage is in pipe/capture scenarios: PowerShell variable assignment, CI capture, | pipes; WSL also prints a proxy-detection banner (stderr) at startup, making it worse.
Cause
wsl.exe emits UTF-16LE (no BOM) when its stdout is a pipe/non-TTY; PowerShell captures it and writes it out with its own stdout encoding (measured: both PS 5.1 and pwsh 7 -Command paths produce UTF-16LE without BOM); the downstream consumer decodes it as UTF-8/single-byte → every ASCII char is separated by 00, Chinese becomes �. This is known wsl.exe behavior, still present in #13093 (WSL 2.5.7, 2025).
Solution
- Set
$env:WSL_UTF8 = "1"in the script before calling wsl (official Microsoft switch; measured: under pwsh -File, wsl output becomes proper UTF-8);
put it at the top of the script, or use cmd /c "set WSL_UTF8=1 && wsl ..." for one-offs.
- Do not hand-build complex/long command lines (escaping and length will bite): write the script into a file inside WSL and run it:
wsl bash /tmp/x.sh; from PowerShell write via UNC: Set-Content \\wsl.localhost\Ubuntu\tmp\x.sh.
- Base64 transport for long/special-char content:
wsl bash -lc "echo <base64> | base64 -d | bash"(verified; bypasses quoting/escaping/length). - Route output through files: have the WSL command write to
/tmp/out.txt, then read withGet-Content \\wsl.localhost\Ubuntu\tmp\out.txt—
reading a UTF-8 file directly avoids the pipe encoding problem entirely.
- Auxiliary:
[Console]::OutputEncoding = [Text.Encoding]::UTF8affects how PowerShell writes to the console; combine with 1. - The WSL startup banner ("localhost proxy detected...") is stderr noise: with WSL_UTF8 it stops garbling; ignore or
2>$nullit.
Verification Evidence
- Local repro (2026-08-29, Windows 11 + WSL Ubuntu, PowerShell 5.1 and pwsh 7):
- Control:
wsl.exe echo hi > filefrom cmd → bytes68 69 0A(UTF-8, fine); - Repro:
pwsh -NoProfile -Command "wsl echo hi" > file→ bytes77 00 73 00 6C 00 ...(UTF-16LE without BOM),
displayed as "w s l :" garbage when decoded as UTF-8 — same as #13093; PowerShell 5.1 reproduces identically;
- Fix: with
$env:WSL_UTF8="1"in a ps1,& wsl.exe echo hi→ bytes77 73 6C 3A 20 E6 A3 80(proper UTF-8, Chinese intact); - Base64 channel:
wsl bash -lc "echo dGVzdA== | base64 -d"→ test (exit 0); - File relay: write /tmp/out.txt inside WSL, read via UNC — no encoding issues (used daily in practice).
Evidence details kept locally (2026-08-29)
Applicable tools (1)
This problem is linked from the tool pages below — reachable from either side.
wslSource & evidence
← Coding Agents & Terminal domain | Problem Catalog Experience | Facing another issue? Go to the Helpdesk