PowerShell可复用排序函数失效问题排查
PowerShell 自定义排序函数失效问题修复
我有一组包含脚本名称的字符串列表,需要完成以下操作:
- 检查脚本版本,筛选出版本高于已执行最新版本的脚本,并按版本对应的执行顺序运行
- 对已执行脚本列表排序,确定其中的最新脚本
我编写了解析版本信息的代码,希望复用Sort-Scripts函数给两个列表排序,但该函数无法正常工作,直接调用Sort-Object却能得到正确结果。相关代码如下:
function Get-SemVer($version) { $version -match '^(?<major>\d+)(\.(?<minor>\d+))?(\.(?<patch>\d+))?(\-(?<pre>[0-9A-Za-z\-\.]+))?(\+(?<build>[0-9A-Za-z\-\.]+))?$' | Out-Null if ($matches) { [PSCustomObject]@{ Major = [int]$matches['major'] Minor = [int]$matches['minor'] Patch = [int]$matches['patch'] PreReleaseLabel = [string]$matches['pre'] BuildLabel = [string]$matches['build'] } } } function ParseVersionDetails { param([string]$InputString) if ($InputString -match '^(?<version>.*?)_(?<sequence>\d+)_?(?<scriptname>.*)') { [PSCustomObject]@{ Version = Get-SemVer $matches['version'] Sequence = [int]$matches['sequence'] ScriptName = $matches['scriptname'] FullName = $InputString } } } function Sort-Scripts { process { @($Input) | Sort-Object -Property @{Expression={$_.Version.Major}}, @{Expression={$_.Version.Minor}}, @{Expression={$_.Version.Patch}}, @{Expression={$_.Version.PreReleaseLabel}}, @{Expression={$_.Version.BuildLabel}}, @{Expression={$_.Sequence}} } } $list = @( "7.9.0-dev-IRIS-Dyn_02_SomeScript", "7.9.0-dev-IRIS-Dyn_01_SomeScript", "7.9.1-prod-IRIS-Dyn+13_01_OtherScript", "7.8.5_02_AnotherScript", "7.8.5_01_AnotherScript" ) "#### Doesn't work" $list | ForEach-Object { ParseVersionDetails -InputString $_ } | Sort-Scripts "#### Works" $list | ForEach-Object { ParseVersionDetails -InputString $_ } | Sort-Object -Property @{Expression={$_.Version.Major}}, @{Expression={$_.Version.Minor}}, @{Expression={$_.Version.Patch}}, @{Expression={$_.Version.PreReleaseLabel}}, @{Expression={$_.Version.BuildLabel}}, @{Expression={$_.Sequence}} "Done"
问题原因
Sort-Scripts函数的process块中,$Input是逐元素接收管道输入的——每次process块执行时,@($Input)里只有当前处理的单个对象。对单个对象排序不会改变任何顺序,因此函数看起来完全没生效。
而直接调用Sort-Object时,它会先收集所有管道输入,再统一执行排序逻辑,所以能得到正确结果。
修复方案
修改Sort-Scripts函数,先收集所有管道输入到数组,再在end块中统一排序。有两种写法:
写法1:用begin/process/end块收集输入
function Sort-Scripts { begin { # 初始化数组存储所有输入对象 $allScripts = @() } process { # 逐元素添加到数组 $allScripts += $_ } end { # 对完整数组执行排序 $allScripts | Sort-Object -Property ` @{Expression={$_.Version.Major}}, @{Expression={$_.Version.Minor}}, @{Expression={$_.Version.Patch}}, @{Expression={$_.Version.PreReleaseLabel}}, @{Expression={$_.Version.BuildLabel}}, @{Expression={$_.Sequence}} } }
写法2:显式声明管道参数(更规范)
function Sort-Scripts { [CmdletBinding()] param( # 声明接收管道输入的参数 [Parameter(ValueFromPipeline = $true)] $InputObject ) begin { $allScripts = @() } process { $allScripts += $InputObject } end { $allScripts | Sort-Object -Property ` @{Expression={$_.Version.Major}}, @{Expression={$_.Version.Minor}}, @{Expression={$_.Version.Patch}}, @{Expression={$_.Version.PreReleaseLabel}}, @{Expression={$_.Version.BuildLabel}}, @{Expression={$_.Sequence}} } }
修改后,Sort-Scripts函数就能和直接调用Sort-Object产生完全一致的排序结果,可复用给多个列表排序。
内容的提问来源于stack exchange,提问作者causa prima
相关产品推荐
相关产品推荐

