Windows PowerShell 编码踩坑记
在中文 Windows 系统上用 PowerShell 处理文本文件,编码问题是一个极其隐蔽又极具破坏力的陷阱。
事故现场
---
title: "鎶€鏈爤鍏ㄦ櫙" # ← 原来是 "技术栈全景"
date: 2026-06-01
---
Hugo 构建报错:
failed to unmarshal YAML: yaml: line 4: found unexpected end of stream
28 个 markdown 文件全部乱码,网站直接崩了。
根因分析
罪魁祸首:Set-Content 的默认编码
Set-Content file.md -Value (Get-Content file.md) # ❌ 危险!
在中文 Windows 上,Set-Content 不带 -Encoding 参数时,默认使用系统的 ANSI 编码(简体中文 = GBK/CodePage 936)。
发生了什么?
| 步骤 | 操作 | 结果 |
|---|---|---|
| 1 | 文件原本是 UTF-8,汉字正常 | 技术栈全景 → E6 8A 80 E6 9C AF... |
| 2 | Get-Content 以 GBK 读取 |
UTF-8 字节被当成 GBK 解码 → 得到错误的 Unicode 字符 |
| 3 | Set-Content 以 GBK 写入 |
错误的 Unicode → GBK 编码 → 写入文件 |
| 4 | Hugo 以 UTF-8 读取 | GBK 字节被当成 UTF-8 → 全部变成乱码 |
为什么 Mac/Linux 没这个问题?
因为 Mac/Linux 的默认编码是 UTF-8,而 Windows 中文版默认是 GBK。跨平台工具链只要在 Windows 上踩到 Set-Content 的默认行为就会中招。
解决方案
✅ 正确写法
# 方式一:显式指定 UTF-8(推荐)
Set-Content file.md -Value $content -Encoding UTF8
# 方式二:WriteAllText(无 BOM,最安全)
[System.IO.File]::WriteAllText("file.md", $content, [System.Text.UTF8Encoding]::new($false))
# 方式三:先读再写,全程指定编码
$content = Get-Content file.md -Encoding UTF8
Set-Content file.md -Value $content -Encoding UTF8
注意:
-Encoding UTF8在 PowerShell 5.x 中会带 BOM(EF BB BF)。某些工具(如 Hugo 0.145.0+)会因 BOM 导致 YAML 解析失败。方式二(WriteAllText+new($false))写入的是无 BOM 的 UTF-8,最安全。
❌ 错误写法(一律禁止)
Set-Content file.md -Value $content # 默认 GBK ❌
Out-File file.md -InputObject $content # 默认 UTF-16 ❌
$content > file.md # 重定向也是默认 GBK ❌
BOM 的额外陷阱
即使 UTF-8 编码正确了,文件开头的 BOM 字节(EF BB BF)也可能导致问题。
| 工具 | 对 BOM 的态度 |
|---|---|
| Hugo < 0.145 | 容忍 BOM |
| Hugo ≥ 0.145 | 拒绝 BOM → YAML 解析失败 |
| Node.js / Go | 通常能处理 |
| Python / Ruby | 部分库会报错 |
最佳实践:一律输出 UTF-8 without BOM。
教训总结
- 在 Windows 上写 PowerShell,永远不要相信默认编码
- 操作文件时始终显式指定
-Encoding UTF8 - 跨平台项目/自动化脚本,用
[System.IO.File]::WriteAllText()+[System.Text.UTF8Encoding]::new($false)最稳妥 - 如果文件里的中文突然变成了
鎶€鏈€之类的乱码,99% 是编码问题 - 修复方法:用正确的编码重新读取并写入
快速诊断命令
# 检查文件前几个字节确认编码
$bytes = [System.IO.File]::ReadAllBytes("file.md")
Write-Output "前 5 字节: $($bytes[0..4] -join ', ')"
# 如果前三个字节是 239, 187, 191 (= EF BB BF) → 有 BOM
# 如果是 GBK 乱码,对比 UTF-8 vs GBK 解码