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 中会带 BOMEF 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


教训总结

  1. 在 Windows 上写 PowerShell,永远不要相信默认编码
  2. 操作文件时始终显式指定 -Encoding UTF8
  3. 跨平台项目/自动化脚本,用 [System.IO.File]::WriteAllText() + [System.Text.UTF8Encoding]::new($false) 最稳妥
  4. 如果文件里的中文突然变成了 鎶€鏈€ 之类的乱码,99% 是编码问题
  5. 修复方法:用正确的编码重新读取并写入

快速诊断命令

# 检查文件前几个字节确认编码
$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 解码