你现在看到Active code page: 936,说明这个CMD窗口的活动控制台代码页已经是936(简体中文GBK/CP 936这一套)。chcp的官方说明也写得很清楚:它改变的是active console code page,并且表里确实把936标成Chinese。(Microsoft Learn)
在这种前提下,你的.bat仍然打印乱码,几乎可以直接锁定成一句话:
你的.bat文件字节是UTF-8,但cmd.exe正在按CP 936去解码脚本内容,所以脚本里写死的中文在进入echo之前就已经被误解码成另一串字符了。
编辑器能显示正确,只能证明文件本身是合法的UTF-8;并不能保证cmd.exe会用UTF-8去读它。cmd.exe对批处理的解释,跟它当前所处终端窗口的代码页强相关;在微软工程师博客的讨论里就有一句非常直白的表述:批处理会在命令解释器运行的终端窗口里,按当时的活动代码页来执行与解释。(Microsoft for Developers)
把乱码还原成一条“字节流水线”,你就知道该改哪里
你脚本里有一行echo 中文,从磁盘到屏幕,至少走这几步:
1)脚本文件编码
你保存为UTF-8,磁盘上每个汉字通常对应 3 个字节的UTF-8序列。
2)cmd.exe读取脚本并解码cmd.exe读到这些字节后,需要用某个“当前代码页”把它变成内部字符。你现在窗口的chcp是936,那它很可能就按CP 936来解释这些字节,于是UTF-8的字节序列会被当成GBK字节序列乱解码,得到一串“看起来像乱码但其实是合法字符”的结果。
3)echo打印echo打印的是第 2 步已经错掉的字符,所以你看到的当然是乱码。注意这里的关键点:乱码不是“打印阶段”才出现的,而是“脚本被解释阶段”就已经发生了。
4)字体渲染
就算前面都对,字体不含中文字形也会变方块或问号。chcp的备注里也专门提醒了Raster fonts与非默认代码页的显示问题,建议使用TrueType字体来保证显示。(Microsoft Learn)
你当前的情况更像第 2 步就错了,因为你明确说脚本编码是UTF-8,而代码页是936,这本身就是一组不匹配。
为什么你已经chcp 936了,它还是不对
很多人会直觉认为:936是中文,那中文就该正常。
这句话只有在一个前提成立时才对:脚本本身也得是CP 936(也就是GBK)编码。
现在你的脚本是UTF-8,等价于你把一封用UTF-8写的信,交给一个只按GBK规则读信的人去读。读出来当然像“每个字都认识但连起来不通”。
所以你面对的不是“936不支持中文”,而是“936支持的是GBK中文,不是UTF-8中文”。
解决方式并不玄学:让脚本编码与cmd 解释脚本的代码页对齐
下面给你几条实战路径,你按自己的工程习惯选就行。
路径 A:保持chcp 936,把.bat存成GBK
这是最稳、最省事、兼容老工具链最好的做法,尤其你现在的窗口已经是936。
你要做的事只有一个:把.bat从UTF-8改存为GBK(也常被编辑器叫做ANSI,在简中代码页语境下通常就是CP 936)。
在VS Code里可以这样操作(不需要任何插件):
- 看右下角编码指示
- 选择
Reopen with Encoding,选GBK - 再
Save with Encoding,选GBK
做完后直接在同一个CMD窗口运行,通常立刻正常。
你可以用这份最小可运行脚本验证,文件本身请保存成GBK:
@echo off chcp 936 >nul echo 中文输出测试:你好,世界 pause这里我仍然保留chcp 936,是为了让脚本在别的窗口里被人双击运行时,也尽量把环境拉回一致。
路径 B:坚持仓库统一UTF-8,那就把代码页切到65001,并把切换放在中文出现之前
如果你希望脚本在Git仓库里统一UTF-8,那就不要让它在936下被解释。
思路是:让CMD用UTF-8代码页来解释后续行。chcp支持把活动控制台代码页改掉。(Microsoft Learn)
可运行示例,文件保存为UTF-8,并且建议用UTF-8 no BOM:
@echo off rem 这两行保持纯 ASCII,避免在切换代码页前出现非 ASCII 字节 chcp 65001 >nul echo 中文输出测试:你好,世界 pause这里有两个很现实的注意点:
- 很多机器上把控制台切到
65001后,外部命令行工具可能会出现不兼容,原因是历史包袱与实现细节,微软自己也在命令行团队博客里长期讨论过控制台内部缓冲区对Unicode/UTF-8的支持演进。(Microsoft for Developers) - 字体仍然重要,
Raster字体组合很容易出显示问题,chcp的备注也点名过这一点。(Microsoft Learn)
如果你不想每个脚本都写chcp 65001,也可以把它做成某个启动入口的默认行为,比如通过快捷方式参数cmd.exe /k chcp 65001之类的方式(微软工程师博客讨论里也有人给出类似做法)。(Microsoft for Developers)
路径 C:让.bat只做调度,把中文输出交给PowerShell
如果你对CMD的代码页机制不想再折腾,最工程化的做法是:批处理只负责流程控制,所有需要可靠Unicode输出的地方交给PowerShell。
PowerShell官方文档对BOM、UTF-8、以及默认输出编码讲得非常细,尤其强调了兼容性角度下尽量避免UTF-8的BOM,并说明PowerShell 6+默认输出是utf8NoBOM。(Microsoft Learn)
你可以直接用下面这个.bat,不依赖控制台当前代码页,实际输出由PowerShell写:
@echo off powershell -NoProfile -Command [Console]::OutputEncoding=[System.Text.Encoding]::UTF8;[Console]::WriteLine('中文输出测试:你好,世界');pause这种写法在“输出中文提示、日志、状态”这类需求上非常省心。
路径 D:系统级把默认代码页改成UTF-8(影响面大,适合个人机,不建议不加评估就推到团队)
Windows 11有个系统设置项Beta: Use Unicode UTF-8 for worldwide language support,微软文档里明确写了路径,并说明需要重启才生效。(Microsoft Learn)
这个开关会把系统层面的旧式代码页语义往UTF-8方向推,优点是很多“默认按本地代码页”的路径会更接近现代生态,缺点是兼容性风险也更大,一些依赖旧代码页行为的软件可能出现异常。你如果只是为了让CMD跑脚本输出中文,我更倾向把它当成备选,而不是必选。
你提到的是“在屏幕上乱码”,顺手补一刀:重定向到文件时是另一套坑
不少人会遇到另一个变体:屏幕显示正常,但> out.txt后文件打开乱码。
这时要记住:cmd自己有/a与/u两个开关,控制命令输出格式化成ANSI还是Unicode。微软的cmd文档在参数表里写得很明确:/a输出按ANSI,/u输出按Unicode。(Microsoft Learn)
它对“你的中文能不能在控制台显示”帮助不大,但对“重定向出来的文件是什么编码”很关键。你以后如果要生成日志文件,这个点能少踩很多坑。
针对你当前信息的直接结论与建议
你已经在CMD里确认Active code page: 936,那就不要再怀疑936是否支持中文,它支持的是GBK中文。(Microsoft Learn)
你脚本是UTF-8,两者不一致,乱码完全符合预期。
我会给你一个最贴近现实的选择逻辑:
- 你的脚本主要在传统
CMD里跑,目标机器也不保证统一用Windows Terminal,并且脚本就是给Windows运维用的:把.bat存成GBK,继续用chcp 936,最稳。 - 你的脚本要进
Git,要跨机器协作,仓库希望统一UTF-8:把脚本存为UTF-8 no BOM,并在脚本最前面用纯ASCII行先执行chcp 65001,中文放在后面,同时选用支持中文的TrueType字体。(Microsoft Learn) - 你想彻底绕开代码页地狱:用
.ps1或在.bat里调用PowerShell输出中文,编码规则更清晰,官方文档也更完整。(Microsoft Learn)
如果你愿意把你那段.bat的最小复现片段贴出来(尤其是文件头几行,以及你是双击运行还是在已打开的CMD里运行),我还能帮你判断有没有混入BOM、字体问题、或是脚本里读取外部文本导致的二次转码问题,从而把方案再收敛到最短路径。