PowerShell 实战踩坑大全:从 GBK 乱码到引号地狱的十二个真实案例

本文是《在精简版 Windows 上恢复 WSL2 与 Docker:一次从检测到落地的全过程实录》的姊妹篇。那次任务在 PowerShell 上踩的坑,单独值得写一篇;文末还把这些坑沉淀成了一个可复用的检查插件powershell-check)——以后凡是涉及 PowerShell 的执行,先启动插件检查一遍再动手。

一、中文控制台 GBK 乱码

wsl/dism 输出的中文全是乱码(形如 g?R?l g?S?e?T?^/T?R?)。原因是控制台代码页是 GBK 而程序输出 UTF-8。每次开头加:

1
chcp 65001 | Out-Null

二、$var: 被解析成”驱动器限定变量”

1
"attempt $attempt: downloading..."   # 报错: ':' was not followed by a valid variable name

PowerShell 把 $attempt: 当成 $env:Path 那种带作用域/驱动器的语法。解法:用 ${} 显式界定

1
"attempt ${attempt}: downloading..."

三、PowerShell 5.1 没有三元运算符

1
2
$v = ($a -gt 0) ? "yes" : "no"   # PS 5.1 直接解析错误!
$v = if ($a -gt 0) { "yes" } else { "no" } # 正确

四、双引号里的 $WINDOWS 被当成变量展开

1
2
Get-Content "C:\$WINDOWS.~BT\Sources\Panther\setupact.log"  # 路径变成 C:\.~BT\...,$WINDOWS 是未定义变量!
Get-Content 'C:\$WINDOWS.~BT\Sources\Panther\setupact.log' # 单引号才对

这个坑导致排查时一度以为日志文件”神秘消失”,浪费了不少时间。

五、用 JS 模板字符串生成 PowerShell 脚本时的转义地狱

在 JS 里写模板字符串生成 .ps1 时,$($var)${attempt} 都会被 JS 抢先插值。规则:所有想保留到 PS 里的 $ 都要写成 \$,否则报 attempt is not defined 这类 JS 错误。

六、Start-Process -ArgumentList 的引号与沙箱坑

  • -ArgumentList 拼接时不会自动加引号,路径含空格会碎参数。
  • 后台任务里 Start-Process curl 下载的文件内容损坏(大小正确、字节全错),直接 & curl.exe 则正常。能直接调用就别包一层。

七、Get-WindowsOptionalFeature 的 FeatureName 不支持数组

1
2
Get-WindowsOptionalFeature -Online -FeatureName A, B   # 报错 Cannot convert 'System.Object[]'
foreach ($n in "A","B") { Get-WindowsOptionalFeature -Online -FeatureName $n }

八、DISM 动词:/Dismount-Image 不存在

正确动词是 /Unmount-Image/Dismount-Image错误: 87),导致 wim 一直挂着占空间。

九、RestoreHealth 的源版本必须 ≤ 当前系统

1
2
dism /Online /Cleanup-Image /RestoreHealth /Source:WIM:install.wim:1
# 错误: 0x800f0915 找不到修复内容 —— 因为 wim 是 8037,系统是 7705,源比系统新

修复源要求同版本或更旧(或走 Windows Update)。

十、微软 CDN 下载加速(正向收获)

  • 单连接 1.5MB/s → 实测 Range 请求单连接 15~20MB/s、突发 71MB/s。
  • 做法:HEAD 拿 Content-Length → 切成 N 段 → 并行 curl -r start-endcmd /c copy /b a+b+c out 合并 → 校验总大小。
  • 注意 curl -L-C - 组合在部分 CDN 上会拖慢速度,极简参数往往更快。

十一、Windows 上 npm 被 ExecutionPolicy 拦截(要用 npm.cmd

1
npm run build   # 报错: File C:\nvm4w\nodejs\npm.ps1 cannot be loaded because running scripts is disabled on this system

Windows 装 Node 后 PATH 里同时存在 npm.ps1npm.cmd,PowerShell 优先解析到 .ps1,而默认执行策略禁止运行脚本。解法:显式调用 npm.cmdnpx.cmdpnpm.cmd 同理):

1
npm.cmd run build

十二、PowerShell 5.1 没有 && / || 链式运算符

1
2
git commit && git push   # PS 5.1 直接解析报错;这是 PS 7+ 才有的语法
git commit; git push # 用分号链式执行

&&/|| 是 PowerShell 7 才引入的;5.1 里链式执行用分号,或按退出码判断:if ($LASTEXITCODE -eq 0) { git push }。注意 cmd /c "a && b" 这类 cmd/bash 内部字符串不受影响。

十三、沉淀成检查插件:powershell-check

这些坑如果只躺在文章里,下次还是会踩。我把它们提取成了一个检查插件powershell-check —— 一个静态检查脚本 + 配套使用规范,位于博客仓库 .dsh/skills/powershell-check/ 下。规则:一旦涉及 PowerShell 的执行(调用 pwsh、生成 .ps1、把命令嵌进脚本模板),先启动插件进行检查,FAIL 修复后再执行。

该插件已按 DeepSeek Harness 官方插件范式升级为独立仓库:dsh-powershell-check —— 原生 Cordis 插件,通过官方 tools/pre-execute 拦截点对每次 pwsh 调用自动检查并拦截(deny reason 即修复指引),同时以 ctx.skills.registerProvider 内置同名技能,无需每次手动启动。

1
2
3
4
5
6
7
8
# 检查命令串
node .dsh/skills/powershell-check/scripts/check-pwsh.mjs -- '<命令原文>'

# 检查 .ps1 脚本文件
Get-Content fix.ps1 -Raw | node .dsh/skills/powershell-check/scripts/check-pwsh.mjs -

# 插件自检(跑内置正反例)
node .dsh/skills/powershell-check/scripts/check-pwsh.mjs --selftest

插件内置规则与本文一一对应:R1 GBK 乱码(§一)、R2 $var: 解析(§二)、R3 三元运算符(§三)、R4 双引号变量展开(§四)、R5 Start-Process 包装(§六)、R6 FeatureName 数组(§七)、R7 DISM 动词(§八)、R8 RestoreHealth 源版本(§九)、R9 CDN 下载参数(§十)、R10 npm.cmd(§十一)、R11 &&/||(§十二);外加一条写作侧规则(§五,JS 转义,由使用规范约束、检查器提示)。维护约定(长期生效):每次 PowerShell 执行踩到新坑,先给检查器加规则(含 selftest 正反例)并跑通 –selftest,再同步补进本文与 SKILL.md,然后走一轮 commit + push + publish 部署,让知识库与工具保持同步。


PowerShell 实战踩坑大全:从 GBK 乱码到引号地狱的十二个真实案例
https://chaggle.github.io/2026/08/14/powershell/powershell-pitfalls/
作者
chaggle
发布于
2026年8月14日
更新于
2026年8月15日
许可协议