花拾录
← 返回知识库

PowerShell 脚本中文乱码甚至语法报错:无 BOM 的 UTF-8 被按 GBK 读

软件工程 / 工具导入2026/09/220 阅读0 评论

你在 Windows 上写了一个 .ps1 脚本,里面带着中文提示、中文路径或中文注释。在编辑器里看着一切正常,可一旦交给计划任务、或者直接双击运行,输出全成了怪字;运气不好时,脚本连一行都不执行,直接抛出一句「字符串缺少终止符」的解析错误。你检查了半天逻辑和括号,最后发现跟逻辑毫无关系。

现象

典型有两种表现,而且经常同时出现。

一是乱码:本该输出的表头、提示语变成一串看不懂的汉字组合,比如「±¸·ÝÍê³É」这种;数字和英文却完全正常。

二是语法报错:PowerShell 直接拒绝解析,报类似下面的错误,一行都不执行:

标记“”的表达式或语句中包含意外的标记。
字符串缺少终止符: "。
在 ... 行 ... 处缺少语句块或类型定义

最诡异的地方在于:同一个文件,在别人的机器上、或者换一款 PowerShell 跑,又好端端的。

根因

问题出在解释器读取文件时用的编码,而不是你写进去的字符本身。

Windows 自带的 Windows PowerShell 5.1(可执行文件叫 powershell.exe)读取 .ps1 脚本时,遵循一条很老旧的规则:只有文件开头带 BOM(字节序标记)时,才按 UTF-8 解码;没有 BOM,就按系统 ANSI 代码页解码。在简体中文 Windows 上,ANSI 代码页就是 GBK(CP936)。

而现在的编辑器——VS Code、Notepad++、以及新版本记事本——默认存的是不带 BOM 的 UTF-8。于是形成一个错配:

  • 文件里实际是 UTF-8 字节;
  • 5.1 却拿 GBK 去解。

GBK 每个汉字占 2 字节,UTF-8 每个汉字占 3 字节。逐字节重新分组后,绝大多数汉字会变成两个毫不相干的字,这就是乱码的来源。更麻烦的是偶尔会解出一个落在 ASCII 范围内的字节——比如正好解出一个 "(0x22)——字符串的引号就此错位,后面的内容被当成字符串或代码,于是整个文件解析崩掉。所以「乱码」和「语法报错」其实是同一个根因的两种表现,只是后者运气更差。

需要区分的是:跨平台的 PowerShell 7(pwsh)默认按 UTF-8 读取,不依赖 BOM,所以在 pwsh 下一切正常。如果某台机器上没出问题,多半是他装了 PowerShell 7。

解决

最直接的办法:把脚本存成「UTF-8 with BOM」,让 5.1 明确知道该用 UTF-8 读。

用编辑器的话,在编码菜单里选 UTF-8 with BOM(VS Code 右下角点编码 → Save with Encoding → UTF-8 with BOM)。

如果是用程序生成脚本(比如用 Python 批量产出 .ps1),写入时指定 utf-8-sig——-sig 的意思就是写入 BOM——并把换行固定为 CRLF:

with open('task.ps1', 'w', encoding='utf-8-sig', newline='\r\n') as f:
    f.write(script_text)

写完务必校验前三个字节,确认 BOM 真的写进去了:

head -c 3 task.ps1 | xxd
# 期望输出:00000000: efbb bf

ef bb bf 就是 UTF-8 的 BOM。如果这里出来的不是这三个字节,说明编辑器或写入方式又把它剥掉了。

如果这个脚本注定只在老环境里跑、又不想折腾编码,还有一条最省事的路:脚本里只输出纯英文标签,中文只出现在它读取的数据文件里。

延伸与预防

这条规则不只属于 .ps1。任何「老工具按 ANSI 读文件」的场合都适用——旧版 CMD、部分 Windows 命令行工具都有类似行为。给它们看的文本,加 BOM 是让它们认出 UTF-8 的通用信号。

反过来,从 Linux 传过来的文本通常不带 BOM,直接交给 5.1 就会踩这条坑。跨平台搬运脚本时,把「换行」和「BOM」当成两个必须单独确认的属性,而不是默认它们会自带。

排查思路上也有一条通用教训:遇到「同一份文件在不同机器上表现不同」,先怀疑读取端的默认假设,而不是文件内容本身。编码、换行、路径分隔符,都是这类「环境默认值」的典型——文件没变,是读它的环境变了。

评论(0)

  • 还没有评论,来抢沙发~

相关文章