随着企业内部 PowerShell 脚本数量增加,「质量参差不齐」的问题必然会浮现出来:满是别名、难以阅读的脚本,写着明文密码的脚本,变量名拼写错误却一直无人察觉的脚本。这些问题最终会以编写者本人离开之后,阅读代码的人为此犯难这种形式表面化。
这类问题中相当一部分,可以通过静态分析工具机械化地检测出来。PowerShell 拥有官方的静态分析模块 PSScriptAnalyzer,只需一条命令即可解析整批脚本。最大的优点在于不用写一行测试代码,从第一天起就能看到效果。
本文按照「首先应启用的规则」「对既有资产的分阶段导入」「在 CI 中自动检查」的顺序,整理将 PSScriptAnalyzer 引入企业内部脚本资产的实务步骤。关于通过测试保障质量的内容,请同时参考《用 Pester 完善 PowerShell 测试 ── 让运维脚本不易损坏的实务方法》。
目标读者・前提环境
| 项目 | 内容 |
|---|---|
| 目标读者 | 希望对企业内部日益增多的 PowerShell 脚本进行机械化质量管理的信息系统・开发负责人 |
| 运行环境 | PSScriptAnalyzer 在 Windows PowerShell 5.1 和 PowerShell 7 上都能运行。要解析的脚本是面向 5.1 还是面向 7,与执行解析一方的版本无关,需要通过 PSUseCompatibleSyntax 单独指定(第 4 章) |
| CI 的前提 | 第 7 章的 CI 示例以 GitHub Actions 的 windows-latest 运行器为前提。调用 Invoke-ScriptAnalyzer 的部分在其他 CI 上也通用,解析本身在非 Windows 运行器上也能执行 |
| 所需权限 | 由于导入方式是 Install-Module -Scope CurrentUser,不需要管理员权限 |
| 示例的验证环境 | 文末分发的示例代码是在 PowerShell 7.6 上运行并验证的 |
1. 先说结论
- PSScriptAnalyzer 是 PowerShell 官方的静态分析模块。用
Invoke-ScriptAnalyzer解析脚本或模块,并报告违反的规则。1 - 每条指摘都带有重大度(Severity)。分为 Error / Warning / Information 三个等级,现实的起点是先把 Error 降为零。1
- 配置集中在
PSScriptAnalyzerSettings.psd1中。把Severity、IncludeRules、ExcludeRules、Rules放进代码仓库,让所有人都能按同一基准解析。2 - 局部抑制的做法是
SuppressMessageAttribute+ 写明理由。在按规则整体排除之前,先考虑缩小范围的抑制是否已经足够。3 - 部分指摘可以用
-Fix自动修复。格式整理则由Invoke-Formatter负责。14 - VS Code 的 PowerShell 扩展内置了 PSScriptAnalyzer。编辑过程中就会当场显示警告,比 CI 更早生效。5
- CI 的失败条件设为「重大度 Error + 点名指定的重要规则」。重大度是按规则各自决定的,检测明文密码的
PSAvoidUsingPlainTextForPassword的重大度是 Warning。如果只以 Error 作为条件,就会被放过。6 - 既有资产采用分阶段导入。顺序是「把 Error 降为零」→「只对变更文件严格把关」→「逐步扩大范围」。
2. 导入 ── 先从一条命令开始
Install-Module -Name PSScriptAnalyzer -Scope CurrentUser
# 汇总解析文件夹下的全部内容
Invoke-ScriptAnalyzer -Path 'D:\Scripts' -Recurse |
Sort-Object Severity, RuleName |
Format-Table Severity, RuleName, ScriptName, Line, Message -AutoSize
# 按重大度掌握件数(盘点的第一步)
Invoke-ScriptAnalyzer -Path 'D:\Scripts' -Recurse |
Group-Object Severity | Select-Object Name, Count
首先执行这两条命令,用数字掌握自家资产处于什么状态。即便出现几百条也不必惊讶,大多数现场一开始都是这样。
返回的是什么。Invoke-ScriptAnalyzer 把每一条指摘作为一个对象返回,具有 Severity、RuleName、ScriptName、Line、Message 等属性。第一条命令会按重大度顺序,把「哪个文件的第几行,触犯了哪条规则,原因是什么」逐行排列出来。第二条命令只返回 Name(Error / Warning / Information)和 Count 两列,请先把这几行数字记录下来。分阶段导入(第 6 章)是否有进展,就用这些数字的变化来衡量。如果一条指摘都没有,两条命令都不会显示任何内容(输出为空 = 合格)。
可以用 Get-ScriptAnalyzerRule 查看可用规则的一览与说明。1
Get-ScriptAnalyzerRule | Select-Object Severity, RuleName, CommonName | Sort-Object Severity
Get-ScriptAnalyzerRule -Name PSAvoidUsingWriteHost | Format-List * # 单条规则的说明
3. 首先见效的指摘 ── 实务中的优先顺序
在数十条规则中,按优先级列出与企业内部脚本质量直接相关的规则。
| 规则 | 重大度 | 检测什么 | 为什么重要 |
|---|---|---|---|
PSAvoidUsingPlainTextForPassword |
Warning | 参数以明文形式接收密码 | 凭据以明文保存,在审计中也会被指出6 |
PSAvoidUsingConvertToSecureStringWithPlainText |
Error | 从明文创建 SecureString | 与上一条同根同源,会使加密失去意义 |
PSUseDeclaredVarsMoreThanAssignments |
Warning | 被赋值但从未被使用过的变量 | 能检测出变量名的拼写错误,属于实质性的 bug 检测 |
PSAvoidUsingInvokeExpression |
Warning | 使用 Invoke-Expression |
会把字符串当作代码执行,成为注入攻击的温床 |
PSUseShouldProcessForStateChangingFunctions |
Warning | 涉及状态变更的函数缺少 -WhatIf |
检测出无法事先确认危险操作的设计 |
PSAvoidUsingCmdletAliases |
Warning | ls、%、? 等别名 |
交互式使用时虽然方便,但会损害脚本的可读性 |
PSUseApprovedVerbs |
Warning | 使用未获批准动词的函数名 | 不遵循 Get-/Set- 等约定的名称难以被人发现 |
PSAvoidGlobalVars |
Warning | 使用全局变量 | 副作用变得难以追踪,也无法编写测试 |
PSUseSingularNouns |
Warning | 使用复数形式的名词(如 Get-Users) |
PowerShell 的命名约定,应使用他人能够推测出的名称 |
请留意重大度这一列。9 条规则中只有 1 条是 Error,其余全部是 Warning。7 就连明文密码的检测(PSAvoidUsingPlainTextForPassword)都是 Warning,因此如果把 CI 的失败条件设为「仅重大度 Error」,这张表里的大部分规则都会被放过。重大度是按规则各自决定的,如果有想要不论重大度都让其失败的规则,需要用规则名单独指定(第 6 章、第 7 章)。要在本地确认,可以执行 Get-ScriptAnalyzerRule | Select-Object Severity, RuleName。
尤其是 PSUseDeclaredVarsMoreThanAssignments,是一条性价比很高的指摘。像是本想赋值给 $fileName,后面却引用了 $fileNmae 这样的拼写错误,会被作为「已赋值却从未被使用的变量」检测出来,因此能够实际捕获只有静态分析才能发现的 bug(另外,由于 PowerShell 的变量名不区分大小写,$fileName 与 $filename 属于同一个变量。这种检测方式能够捕获的,是拼写本身不同的情况)。
4. 用配置文件固定团队标准
如果每个人都按各自的标准解析,那就失去了意义。把 PSScriptAnalyzerSettings.psd1 放进代码仓库,让所有人和 CI 都使用同一份配置。2
# PSScriptAnalyzerSettings.psd1
@{
# 使用默认的规则集
IncludeDefaultRules = $true
# 分阶段导入的第 1 阶段仅限定为 Error 和 Warning
Severity = @('Error', 'Warning')
# 作为公司内部方针,暂时搁置的规则(理由以注释形式保留)
ExcludeRules = @(
'PSAvoidUsingWriteHost' # 交互式工具较多,目前予以容许
'PSUseSingularNouns' # 无法一次性改掉既有的函数名
)
# 按规则设置的详细配置
Rules = @{
PSUseCompatibleSyntax = @{
# 检查需要同时在 5.1 和 7 上运行的一批脚本。
# TargetVersions 只能指定规则本身具有语法定义的版本
# (可用 Get-ScriptAnalyzerRule 确认)。写入不支持的值会导致
# 读取配置时出错,需要注意
Enable = $true
TargetVersions = @('5.1', '7.0')
}
PSPlaceOpenBrace = @{
Enable = $true
OnSameLine = $true
NewLineAfter = $true
IgnoreOneLineBlock = $true
}
PSUseConsistentIndentation = @{
Enable = $true
IndentationSize = 4
Kind = 'space'
}
}
}
Invoke-ScriptAnalyzer -Path . -Recurse -Settings .\PSScriptAnalyzerSettings.psd1
这份配置文件,VS Code 的扩展也会读取。PowerShell 扩展的设置项 powershell.scriptAnalysis.settingsPath 的默认值就是 PSScriptAnalyzerSettings.psd1,因此只要以这个名字放在代码仓库根目录下,编辑过程中出现的警告与 CI 的判定基准就会自动保持一致。8 如果要改用别的名字,或者放到子文件夹中,请在该设置项中明确指定路径。一旦这里出现偏差,就会出现「本地什么都没提示,CI 却失败了」的情况,好不容易得到的即时反馈也会因此失去可信度。
PSUseCompatibleSyntax 在 5.1 与 7 混用的环境中格外有用。它能够在执行之前,检测出把 7 专用语法(三元运算符、管道链式运算符等)误写进面向 5.1 的脚本这类事故。迁移方针本身,请参考《Windows PowerShell 5.1 与 PowerShell 7 的差异 ── 企业内部脚本迁移实务指南》。
5. 例外要「附带理由」留存
对于无论如何都无法遵从指摘的地方,不要禁用整条规则,而只抑制那个具体位置。3
function Show-KsBanner {
# 目的是在交互式工具中做装饰性显示,因此有意使用 Write-Host
[Diagnostics.CodeAnalysis.SuppressMessageAttribute(
'PSAvoidUsingWriteHost', '',
Justification = '专供交互式执行使用的显示函数,设计上不返回值')]
[CmdletBinding()]
param([string] $Title)
Write-Host ('=' * 60) -ForegroundColor Cyan
Write-Host $Title -ForegroundColor Cyan
}
关键在于必须写明 Justification。没有理由的抑制,在后来阅读代码的人看来,与「只是单纯消掉了警告」无法区分。这就像是写在代码里的 ADR(架构决策记录),其思路与《ADR(Architecture Decision Record)入门 —— 在小规模开发中,用最简方法留住「为什么这样设计」》相通。
6. 对既有资产的分阶段导入
面对数百条警告,若想着「先全部改完再导入」,基本上会半途而废。因此需要分阶段进行。
第 1 阶段: 先止血(1 天)
只把 Severity = 'Error' 纳入 CI 判定,把这一部分降为零。这里需要注意的是,重大度是按规则各自决定的,与直觉未必一致。例如 PSAvoidUsingPlainTextForPassword 的重大度是 Warning,如果只以 Error 作为失败条件,就不会被触发。6 对于凭据相关这类「不论重大度都想让其失败」的规则,需要像下面这样用规则名明确指定,加入失败条件。
# 先获取解析结果
$issues = Invoke-ScriptAnalyzer -Path . -Recurse -Settings .\PSScriptAnalyzerSettings.psd1
# 把重大度 Error + 单独指定的重要规则,作为 CI 的失败条件
$mustFix = @(
'PSAvoidUsingPlainTextForPassword'
'PSAvoidUsingConvertToSecureStringWithPlainText'
'PSAvoidUsingUsernameAndPasswordParams'
)
$blocking = $issues | Where-Object { $_.Severity -eq 'Error' -or $_.RuleName -in $mustFix }
第 2 阶段: 守住新增与变更部分(1 周) 只把已变更的文件作为解析对象。即便既有的技术债保持原样,也能阻止新问题继续增加。
在 CI 中执行时,用于比较的提交必须已经被获取到本地。actions/checkout 默认只获取 1 个提交,因此需要指定 fetch-depth: 0,或者显式 fetch 基准分支(不这样做会因 unknown revision 而失败)。
还有一点,不要把比较对象固定为 main。git diff A...HEAD 表示「A 与 HEAD 的共同祖先起算的差异」,因此如果在指向 develop 或发布分支的 PR 中使用 origin/main...HEAD,就会把该 PR 未触及的变更也纳入解析范围,导致 CI 因无关文件中既有的指摘而失败。在 GitHub Actions 中,PR 的目标分支会写入 GITHUB_BASE_REF,因此使用这个变量。9
此外,务必检测 git 的失败。PowerShell 默认情况下,即使外部命令返回非零的退出代码,也不会成为终止错误。10 因此,在基准 ref 尚未获取的状态下,git diff 会执行失败,只是输出变为空,后续处理会将其解释为「没有变更文件」,从而导致一个文件都没有解析、CI 却显示为绿色通过。这是差分检查中最危险的一种损坏方式。请在命令执行后立即检查 $LASTEXITCODE,并显式地使其失败(PowerShell 7.3 及以后,也可以设置 $PSNativeCommandUseErrorActionPreference = $true)。10
- uses: actions/checkout@v4
with:
fetch-depth: 0 # 获取差分需要完整的提交历史
# 只解析发生变更的 ps1/psm1(CI 中的差分检查)
# 不要把比较对象固定为 main。在指向 develop 或发布分支的 PR 中,
# 与 main 的差分会包含无关的变更,导致未触及的文件也让 CI 失败。
# PR 的目标分支可以从 GITHUB_BASE_REF 取得(push 触发时为空)
$base = if ($env:GITHUB_BASE_REF) { "origin/$($env:GITHUB_BASE_REF)" } else { 'origin/main' }
# 不加 -c core.quotePath=false 的话,包含日语的路径
# 会以引号+八进制转义的形式返回(如 "scripts/\346..."),导致扩展名判断漏判
$diff = git -c core.quotePath=false diff --name-only "$base...HEAD"
# 原生命令的失败,默认不会成为终止错误。基准 ref 未获取时,
# git 会执行失败导致输出为空,从而被当作"无变更 = 解析对象为零 = 合格"而被放过。
# 执行后立即查看 $LASTEXITCODE,显式地使其失败
if ($LASTEXITCODE -ne 0) {
throw "git diff 执行失败 (exit $LASTEXITCODE)。基准分支 $base 可能尚未获取"
}
$changed = $diff |
Where-Object { $_ -match '\.ps(m|d)?1$' } | # 以 .ps1 / .psm1 / .psd1 为对象
Where-Object { Test-Path $_ }
# -Path 是接收单个路径的参数,如果直接传入数组,
# 会在参数绑定阶段失败。因此逐个文件解析并汇总结果
$issues = foreach ($file in $changed) {
Invoke-ScriptAnalyzer -Path $file -Settings .\PSScriptAnalyzerSettings.psd1
}
第 3 阶段: 扩大范围(持续进行)
逐一去掉 ExcludeRules 中的规则,每处理完一条就相应地收紧一分。借着重构的机会修正既有文件,逐步减少技术债。
能自动修复的指摘,可以用 -Fix 批量处理(应用前请务必确认差分)。1 如果只是想整理格式,可以使用 Invoke-Formatter。4
Invoke-ScriptAnalyzer -Path .\Scripts -Recurse -Fix -Settings .\PSScriptAnalyzerSettings.psd1
git diff # 务必用肉眼确认改动了什么
7. 在 CI 中自动化
如果是 GitHub Actions,在 Windows 运行器上只需几行代码即可实现。要点是让 Error 使流程失败,Warning 则仅作展示。
name: powershell-lint
on:
pull_request:
paths: ['**/*.ps1', '**/*.psm1', '**/*.psd1']
jobs:
analyze:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # 切换为差分解析时需要(第 6 章)
- name: Install PSScriptAnalyzer
shell: pwsh
run: |
Set-PSRepository -Name PSGallery -InstallationPolicy Trusted
Install-Module PSScriptAnalyzer -Scope CurrentUser -Force
- name: Analyze
shell: pwsh
run: |
$issues = Invoke-ScriptAnalyzer -Path . -Recurse `
-Settings ./PSScriptAnalyzerSettings.psd1
# 把全部结果输出到日志(让警告也能被看到)
$issues | Sort-Object Severity, ScriptName, Line |
Format-Table Severity, RuleName, ScriptName, Line, Message -AutoSize |
Out-String -Width 200 | Write-Host
# 失败条件 = 重大度 Error + 不论重大度都不容许的规则
$mustFix = @(
'PSAvoidUsingPlainTextForPassword'
'PSAvoidUsingConvertToSecureStringWithPlainText'
'PSAvoidUsingUsernameAndPasswordParams'
)
$blocking = @($issues | Where-Object { $_.Severity -eq 'Error' -or $_.RuleName -in $mustFix })
$warns = @($issues | Where-Object Severity -eq 'Warning')
Write-Host "Blocking: $($blocking.Count) / Warning: $($warns.Count)"
# 分阶段导入推进后,也把 Warning 纳入判定条件
if ($blocking.Count -gt 0) {
throw "存在 $($blocking.Count) 条需要修复的指摘"
}
把它和 Pester 测试整合进同一个工作流,就能建立起「lint 通过 → 测试通过 → 才能合并」这样的流程。关于 Windows 应用 CI/CD 整体的搭建方式,请参考《WinForms / WPF 应用的 CI/CD 实践 ── 用 GitHub Actions 实现从构建到签名、发布的自动化》。
即便没有 CI 环境,只要每月运行一次 Invoke-ScriptAnalyzer 并把结果保存为 CSV,也足以让资产状态可视化。
Invoke-ScriptAnalyzer -Path '\\fileserver\scripts' -Recurse |
Select-Object Severity, RuleName, ScriptName, Line, Message |
Export-Csv "D:\盘点\lint_$(Get-Date -f yyyyMM).csv" -Encoding utf8BOM -NoTypeInformation
8. 实务定式(判断表)
| 论点 | 选项 | 判断标准 |
|---|---|---|
| 导入顺序 | 从 Pester 开始 / 从 PSScriptAnalyzer 开始 | 静态分析不用写测试,从第一天起就能见效 |
| 最初的对象 | 全部规则 / Severity=Error + 点名指定凭据相关规则 | 把全部规则都作为条件,谁都通不过。注意重大度是按规则各自决定的6 |
| 既有的大量警告 | 全部修复 / 只对变更文件严格把关 | 先止住增长,再伺机逐步减少 |
| 局部例外 | ExcludeRules / SuppressMessageAttribute + Justification |
把影响范围降到最小,必须留下理由3 |
| 配置的共享 | 各自的配置 / 代码仓库中的 .psd1 |
让 CI 与开发者的标准保持一致2 |
| 5.1 与 7 混用 | 实际运行确认 / PSUseCompatibleSyntax |
在执行前检测出语法层面的不兼容 |
| 自动修复 | 手工处理 / -Fix + 确认差分 |
应用后务必查看 git diff1 |
| 编辑时的反馈 | 仅靠 CI / VS Code 扩展 | 当场就能修正,成本最低5 |
9. 总结
- PSScriptAnalyzer 是官方的静态分析模块,不用写测试即可导入,从第一天起就能见效。
- 先把 Error 降为零,再只对变更文件严格检查,这种分阶段导入方式是现实可行的。由于重大度是按规则各自决定的,像明文密码(Warning)这类想让其失败的规则,需要用规则名加入失败条件。
- 有些规则,如
PSUseDeclaredVarsMoreThanAssignments,能够捕获变量名拼写错误这种实际存在的 bug。 - 把配置集中放进
PSScriptAnalyzerSettings.psd1并置于代码仓库中,使开发者与 CI 的标准保持一致。 - 例外情况在
SuppressMessageAttribute中写明理由后留存。按规则整体排除是最后的手段。 - 在 CI 中让 Error 使流程失败,Warning 只做可视化。把它和 Pester 放进同一个工作流,质量把关就能集中到一处。
示例代码下载
本文涉及的代码,已整理成可以直接运行的形式进行分发,其中包含配置文件、CI 合格与否的判定脚本,以及 GitHub Actions 的示例。
本文的示例,是实际在 PowerShell 7.6 上运行并验证过的(Pester 测试 14 项)。执行 zip 中包含的 Invoke-SampleTests.ps1,即可在您本地重现同样的验证。
# 语法解析 + 静态分析 + Pester 测试
./Invoke-SampleTests.ps1
配置值(路径、服务器名、租户 ID 等)均为示例。请勿直接在生产环境中运行,请结合自身环境进行相应调整后再使用。
相关文章
- 用 Pester 完善 PowerShell 测试 ── 让运维脚本不易损坏的实务方法
- PowerShell 脚本的参数设计与模块化 ── 从「能运行的脚本」到「可以交给别人的脚本」
- PowerShell 中凭据的安全处理方式 ── 把明文密码逐出脚本
- Windows PowerShell 5.1 与 PowerShell 7 的区别 ── 企业内部脚本迁移实务指南
- WinForms / WPF 应用的 CI/CD 实践 ── 用 GitHub Actions 实现从构建到签名、发布的自动化
- ADR(Architecture Decision Record)入门 —— 在小规模开发中,用最简方法留住「为什么这样设计」
相关咨询领域
合同会社小村软件承接企业内部脚本资产的盘点与质量标准制定、静态分析・测试的 CI 导入,以及对因人而异、过度依赖个人经验的运维脚本进行可维护性改善。
参考链接
-
Microsoft Learn,PSScriptAnalyzer 模块概览。关于 PSScriptAnalyzer 是面向 PowerShell 脚本・模块的静态分析工具,Invoke-ScriptAnalyzer 的解析方式及 -Path / -Recurse / -Settings / -Fix / -ExcludeRule 等参数,用 Get-ScriptAnalyzerRule 获取规则一览,以及诊断结果带有重大度(Error / Warning / Information)等内容。 ↩ ↩2 ↩3 ↩4 ↩5 ↩6
-
Microsoft Learn,PSScriptAnalyzer 的配置文件。关于可在配置文件(.psd1)中指定 Severity・IncludeRules・ExcludeRules・IncludeDefaultRules・Rules 等,可通过 -Settings 参数传入配置文件,以及按规则的详细设置(PSUseCompatibleSyntax 的 TargetVersions、格式化类规则的选项)等内容。 ↩ ↩2 ↩3
-
Microsoft Learn,PSScriptAnalyzer 的规则抑制。关于用 System.Diagnostics.CodeAnalysis.SuppressMessageAttribute 按规则单位・对象单位抑制诊断,以及 RuleName・Target・Justification 各参数的内容。 ↩ ↩2 ↩3
-
Microsoft Learn,Invoke-Formatter。关于根据配置整理脚本文本格式,以及可在配置文件中指定格式化规则(缩进、开括号的位置、空白的处理方式等)的内容。 ↩ ↩2
-
Microsoft Learn,在 Visual Studio Code 中使用 PowerShell。关于 PowerShell 扩展利用 PSScriptAnalyzer 在编辑过程中显示警告,以及提供格式设置功能等内容。 ↩ ↩2
-
Microsoft Learn,AvoidUsingPlainTextForPassword。关于不应以明文字符串类型参数接收密码或机密信息,而应使用 SecureString 或 PSCredential,以及该规则的重大度(Severity Level)为 Warning 且始终启用等内容。相关规则 AvoidUsingConvertToSecureStringWithPlainText(从明文生成 SecureString 无法保护机密信息)也请一并参考。 ↩ ↩2 ↩3 ↩4
-
Microsoft Learn,PSScriptAnalyzer 规则一览。关于内置规则一览表中列出的各规则重大度(Severity)、是否默认启用、是否可配置等内容。正文表格中列出的规则重大度(AvoidUsingConvertToSecureStringWithPlainText 为 Error,AvoidUsingPlainTextForPassword・UseDeclaredVarsMoreThanAssignments・AvoidUsingInvokeExpression・UseShouldProcessForStateChangingFunctions・AvoidUsingCmdletAliases・UseApprovedVerbs・AvoidGlobalVars・UseSingularNouns 为 Warning),以及第 6 章、第 7 章中点名的 AvoidUsingUsernameAndPasswordParams 为 Error,均基于该一览表及各规则的独立页面。 ↩
-
PowerShell/vscode-powershell,package.json(扩展的设置定义)。关于 powershell.scriptAnalysis.settingsPath 是指定 PSScriptAnalyzer 配置文件路径的设置项,其默认值为
PSScriptAnalyzerSettings.psd1,以及 powershell.scriptAnalysis.enable 可切换编辑过程中实时解析的启用与禁用等内容。 ↩ -
GitHub Docs,Variables reference ─ Default environment variables。关于在 pull_request 事件中 GITHUB_BASE_REF 会写入 PR 的目标分支名(在其他事件中为空)的内容。三点省略号写法的含义(从明确指定的两个 ref 的合并基准起算的差异)请参见 Git 官方的 git diff。 ↩
-
Microsoft Learn,about_Preference_Variables ─ $PSNativeCommandUseErrorActionPreference。关于原生命令的非零退出代码默认不会成为终止错误,PowerShell 7.3 引入的该设置在设为 $true 后会遵循 $ErrorActionPreference 成为终止错误,以及可通过 $LASTEXITCODE 获取最近一次外部命令的退出代码等内容。 ↩ ↩2
相关文章
共享相同标签的最新文章。可以围绕相近的主题进一步加深理解。
不再使用 Write-Host ── PowerShell 的输出流与日志设计
本文整理 PowerShell 六个输出流的用法区分、Write-Host 存在的问题与正确的使用场景、函数返回值被污染的原因、通过 -Verbose 与 -InformationVariable 实现调用方控制,以及结构化日志的留存方法。
PowerShell 模块的内部分发与更新 ── PSResourceGet 与内部仓库
本文整理了如何摆脱复制共享文件夹中的 ps1 反复使用的运维方式。内容涵盖模块清单的编写方法、版本管理、用 PSResourceGet 构建内部仓库并进行分发与更新,直至与签名的配合使用。
PowerShell脚本运行慢时该看哪里 ── 数组・管道・匹配的诀窍
整理 PowerShell 脚本变慢的常见原因。从数组 += 导致 O(n^2) 的原理、管道与 foreach 的差异、匹配的哈希表化、文件 I/O 的改善,到正确的测量方法,从实务角度进行讲解。
PowerShell 的并行处理 ── ForEach-Object -Parallel 与作业(Job)的选用之道
本文从实务角度整理 ForEach-Object -Parallel、Start-ThreadJob、Start-Job 的区别与选用之道,$using: 与线程安全性,ThrottleLimit 的确定方法,以及反而会变慢的情形。
从 PowerShell 正确调用外部 exe ── 参数引用、退出代码与乱码的陷阱
从 PowerShell 调用 robocopy 或公司内部 EXE 时,参数会损坏、拿不到退出代码、输出还会出现乱码。本文从实务角度整理 PowerShell 7.3 的参数传递变更、停止解析令牌 --%、以及 Start-Process 的使用区分。
相关主题
与本文相近的主题页面。以本文为起点,可以进一步了解相关服务和其他文章。
Windows 技术主题
汇整 KomuraSoft LLC 关于 Windows 开发、故障调查与既有资产活用文章的主题中心。
常见问题
汇总了咨询这一主题时常见的问题。
- 对既有脚本运行 PSScriptAnalyzer 后,出现了数百条警告,应该从哪里入手?
- 请不要一开始就想着把全部问题都改完。实务上可行的做法是,先只针对重大度为 Error 的指摘,把它降为零。需要注意的是,重大度是按规则各自决定的,例如检测明文密码的 PSAvoidUsingPlainTextForPassword 的重大度是 Warning。如果存在像凭据相关规则这样、不论重大度都想让 CI 失败的规则,请在 CI 的失败条件中明确写出规则名加入判断。接下来,把只对今后要修改的文件进行解析的规则纳入 CI,阻止新问题继续增加。至于既有的警告,现实的做法是先决定「现阶段予以容许」并在配置文件中排除,再借着重构的机会逐一减少。
- 只想抑制特定位置的警告,应该怎么做?
- 可以在对应的函数或脚本上添加 SuppressMessageAttribute。在 System.Diagnostics.CodeAnalysis.SuppressMessageAttribute 中指定规则名,并在 Justification 中写明理由。写明理由很重要,这样之后阅读代码的人才能判断「为什么这里是例外」。如果想禁用整条规则,可以写在配置文件的 ExcludeRules 中,但这样影响范围较大,请先考虑用局部抑制是否已经足够。
- 使用 Write-Host 会出现警告,是不是不能用?
- PSAvoidUsingWriteHost 这条规则指出的问题是,在本应返回值的场合使用 Write-Host,会导致输出无法被取出。如果目的是在交互式工具中做装饰性显示,用 SuppressMessageAttribute 写明理由并抑制警告是合理的做法。另一方面,如果是在无人值守执行的脚本中只使用了 Write-Host,那么按照警告的提示重新审视确实有价值。请不要机械地照搬规则,而要理解指摘背后的意图再做判断。
- Pester 和 PSScriptAnalyzer 应该先引入哪一个?
- 先引入 PSScriptAnalyzer 相对于投入成本能带来更大的效果。完全不用写测试代码,一条命令就能解析全部脚本,从第一天起就能见效。Pester 需要花时间编写测试,起步会慢一些,但能够守护逻辑正确性的手段只有测试。建议的顺序是:先把静态分析纳入 CI,阻止明显的问题,然后从「一旦损坏就会造成困扰」的处理开始,逐步补充 Pester 测试。
- 没有 CI 服务器的小团队,引入这套做法还有意义吗?
- 有意义。即使没有 Git 或 CI,只要对共享文件夹中的全部脚本执行 Invoke-ScriptAnalyzer -Path . -Recurse,就能完成一次盘点。哪怕只是把结果导出为 CSV,每月查看一次「重大度 Error 有多少条」,也能让资产状态可视化。此外,VS Code 的 PowerShell 扩展内置了 PSScriptAnalyzer,编辑过程中就会当场显示警告。仅凭这一点,书写习惯也会切实得到改善。