如何在PSScriptAnalyzer自定义PowerShell规则中获取脚本文件名?
为PSScriptAnalyzer自定义规则传入目标脚本文件名的方法
问题背景
环境要求脚本仅使用ASCII编码,禁止任何Unicode字符,因此需要创建一个与PSScriptAnalyzer原生UseBOMForUnicodeEncodedFile规则作用相反的自定义PowerShell规则,用于检测脚本文件是否存在字节顺序标记(BOM)。
但根据PSScriptAnalyzer自定义规则文档要求,每个规则函数必须包含Token数组或Ast参数,而检测BOM需要获取目标脚本的文件名。原生UseBOMForUnicodeEncodedFile.cs规则虽实现了此功能,但缺乏C#知识无法转换为PowerShell代码,已编写的部分规则代码无法正确传入文件名。
解决方案
PSScriptAnalyzer的自定义规则函数可以通过[System.Management.Automation.Language.ScriptBlockAst]类型的参数,从Ast对象的Extent.File属性中获取目标脚本的完整路径。无需单独定义$FileName参数,只需将规则函数的参数替换为Ast参数,再从Ast中提取文件名即可。
修正后的完整规则代码
#Requires -Version 3.0 function Measure-AvoidByteOrderMark { <# .SYNOPSIS 检测脚本文件是否包含字节顺序标记(BOM) .DESCRIPTION 当脚本文件存在BOM时触发警告,符合ASCII编码无BOM的环境要求 .INPUTS [System.Management.Automation.Language.ScriptBlockAst] .OUTPUTS [Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord] #> [CmdletBinding()] [OutputType([Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord])] Param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.Management.Automation.Language.ScriptBlockAst]$Ast ) Begin { Function Get-Encoding { param([Parameter(ValueFromPipeline=$True)] $Path) process { $Reader = [System.IO.StreamReader]::new($Path, [System.Text.Encoding]::GetEncoding('ISO-8859-1'), $True) [Void]$Reader.Peek() $encoding = $Reader.CurrentEncoding $Reader.Close() return $encoding } } } Process { $FileName = $Ast.Extent.File if (-not $FileName) { return } $Encoding = Get-Encoding $FileName $ByteOrderMark = $Encoding.GetPreamble() # 检测是否存在BOM(非空字节数组即为存在BOM) if ($ByteOrderMark.Count -gt 0) { # 创建脚本位置信息,定位到文件开头(BOM所在位置) $StartPosition = [System.Management.Automation.Language.ScriptPosition]::new( $FileName, 1, 1, $Encoding.HeaderName ) $EndPosition = [System.Management.Automation.Language.ScriptPosition]::new( $FileName, 1, $ByteOrderMark.Count + 1, $Encoding.HeaderName ) $Extent = [System.Management.Automation.Language.ScriptExtent]::new($StartPosition, $EndPosition) [Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord]@{ Message = "文件 $FileName 包含 $($Encoding.HeaderName) 字节顺序标记(BOM),不符合ASCII编码要求。" Extent = $Extent RuleName = 'AvoidByteOrderMark' Severity = 'Warning' RuleSuppressionID = $Encoding.HeaderName } } } } Export-ModuleMember -Function Measure-*
关键修改说明
- 参数替换:将原
$FileName参数替换为$Ast(ScriptBlockAst类型),符合PSScriptAnalyzer规则要求。 - 获取文件名:通过
$Ast.Extent.File提取目标脚本的完整路径。 - BOM检测逻辑:直接判断
GetPreamble()返回的字节数组长度,非空则表示存在BOM,无需依赖编码器回退类型判断。 - 位置信息修正:创建
ScriptPosition时传入正确的文件名,定位BOM所在的文件起始位置,确保诊断记录的位置准确。
内容的提问来源于stack exchange,提问作者iRon
相关产品推荐
相关产品推荐

