下面擷圖是我最近遇到的奇妙狀況。想寫 PowerShell 解析 netsh 的輸出文字,netsh 在中文版 Windows 預設會輸出中文,直接跑顯示的是中文內容,用 Pipe | Select-String '-' 抓到中文亂碼,這問題之前遇過不意外,後來試了改用 CoreUtils 的 grep.exe,神奇的事發生了 - grep 成功抓到文字,但 netsh 這回輸出的是英文。

莫非 netsh 遇到 grep 一見如故,兩人改用家鄉話聊天,開始說英文?

在 CMD 做了類似測試,netsh 原本顯示中文 [1],串 | more 因編碼問題出現亂碼 [2],用 | wc 改串 CoreUtils 的 wc.exe 可正確執行 [3],之後直接跑 netsh 不串接管線也改輸出英文 [4],此時再 | more 也是說英文...

而下面這個實驗更有趣,執行過一次 CoreUtils 的 sleep.exe 後,netsh 便改說英文,所以串接管線不是關鍵,CoreUtils 工具(grep、wc、sleep... )執行時似乎會修改某個全域設定,改變了 netsh 的輸出行為。

至於 CoreUtils 改了什麼?這問題太深奧超出我的知識範圍,以前遇到只能問 Google 大神,找對關鍵字爬文找答案可能得耗上數小時。2026 年,新時代的射茶包方法是設計一組可重視問題的步驟,呼叫 AI Agent,請他自己查資料做實驗找答案。

微軟官方建議確保 Console 編碼正確的主流做法是呼叫 SetConsoleCP 及 SetConsoleOutputCP API 將 CodePage 設定為 65001 (CP_UTF8),CoreUtils 是十分新的專案,很有可能會依循此原則在執行時呼叫 SetConsoleCP/SetConsoleOutputCP,以下的 PowerShell 實驗證明了這點。

Add-Type -TypeDefinition @"
using System;
using System.Runtime.InteropServices;
public class CP {
    [DllImport("kernel32")] public static extern uint GetConsoleCP();
    [DllImport("kernel32")] public static extern uint GetConsoleOutputCP();
}
"@

Write-Host "GetConsoleCP=$([CP]::GetConsoleCP()) GetConsoleOutputCP=$([CP]::GetConsoleOutputCP())"
Write-Host "[Console]::OutputEncoding.CodePage=$([Console]::OutputEncoding.CodePage)"
Write-Host "Running sleep.exe..." -ForegroundColor Yellow
& "C:\Program Files\coreutils\bin\sleep.exe" 1
Write-Host "GetConsoleCP=$([CP]::GetConsoleCP()) GetConsoleOutputCP=$([CP]::GetConsoleOutputCP())"
Write-Host "[Console]::OutputEncoding.CodePage=$([Console]::OutputEncoding.CodePage)"

執行完 sleep.exe,GetConsoleOutputCP 被改成 65001,兇手現身! 改了 ConsoleOutputCP 但沒還原,這是 Feature 還是 Bug,就見仁見智了。

留下的問題是:OutputEncoding 背後也是 ConsoleOutputCP,為什麼明明改了 PowerShell 讀到的還是舊的?(先前自己調查我卡在這點鬼打牆,AI 的知識儲備與查資料速度是我的 N 倍,一下子就突破盲點)

MS Learn 官方文件有答案:

Starting with the .NET Framework 4, a property get operation may return a cached value instead of the console's current output encoding. This can occur if the value of the OutputEncoding property is modified by some means other than an assignment to the OutputEncoding property, such as calling the Windows SetConsoleOutputCP function. (重點翻譯:從 .NET 4 起 OutputEncoding 值會有快取,不會反映透過 SetConsoleOutputCP API 做的修改。)

完美破案~

CoreUtils for Windows unexpectedly changes the console output code page to UTF-8, causing netsh to switch from Chinese to English output. The post investigates this behavior with PowerShell experiments and explains why .NET’s cached Console.OutputEncoding can hide the change.


Comments

# by Vinix

MS Learn的引用中出現一句中文「從 .NET 4 起 OutputEncoding 值會有快取,不會反映透過 SetConsoleOutputCP API 做的修改。」,感覺應該是放錯位置。 之前也有遇到類似的狀況,現在才知其根本原因,非常感謝前輩的熱心研究。

# by Jeffrey

to Vinix,那句是我附註的重點翻譯,已補上區隔。 謝謝你留言,發現原來其他人也踩過,分享是有價值的,感覺挺好,呵。

Post a comment