如何在PowerShell动态参数中实现ArgumentCompleter?
动态参数中实现自定义排序的ArgumentCompleter
问题背景
网上有不少PowerShell基础动态参数的示例,也有少量多动态参数的实现案例,但缺少在动态参数中实现ArgumentCompleter并自定义排序的相关示例。
原代码中,动态参数-DataStore使用ValidateSet实现补全,但ValidateSet会自动按字母排序,无法满足按剩余空间从多到少列出数据存储的需求。尝试改用ArgumentCompleterAttribute绑定到动态参数时,补全完全失效,仅ValidateScript能正常校验输入。
原代码(使用ValidateSet的版本)
Param( [Parameter( Position = 0, Mandatory = $True, HelpMessage = 'Choose a VM host for the VM(s).' )] [ValidateScript( { If ($null -eq $global:DefaultVIServer) { Write-Host -BackgroundColor Yellow -ForegroundColor Red -Object 'Connect to vCenter!' Break } Else { If ($PSItem -in ((Get-VMHost).Name)) { $True } Else { Throw "Accepted values: $(((Get-VMHost).Name) -join ', ')" } } } )] [ArgumentCompleter( { Param ($Cmd, $Param, $ParamComplete) [Array]$ValidValues = (Get-VMHost | Sort-Object CpuUsageMhz).Name $ValidValues -like "$ParamComplete*" } )] [String]$VMHost ) DynamicParam { ## Dynamic DataStore. $DynamicDataStore = 'DataStore' $Attributes = New-Object -TypeName System.Management.Automation.ParameterAttribute $Attributes.Position = 1 $Attributes.Mandatory = $True $AttributeCollection = New-Object -TypeName 'System.Collections.ObjectModel.Collection[System.Attribute]' $AttributeCollection.Add($Attributes) $HostDataStores = (Get-VMHost -Name $VMHost | Get-Datastore | Sort-Object FreeSpaceGB -Descending).Name $ValidateSetAttribute = New-Object -TypeName System.Management.Automation.ValidateSetAttribute($HostDataStores) $AttributeCollection.Add($ValidateSetAttribute) $DynamicParamater = New-Object -TypeName System.Management.Automation.RuntimeDefinedParameter($DynamicDataStore, [String], $AttributeCollection) $DynamicParameterDictionary = New-Object -TypeName System.Management.Automation.RuntimeDefinedParameterDictionary $DynamicParameterDictionary.Add($DynamicDataStore, $DynamicParamater) Return $DynamicParameterDictionary } Begin { # Make the dynamic parameter available for the auto-complete. $DataStore = $PSBoundParameters[$DynamicDataStore] }
无效尝试(使用ArgumentCompleterAttribute的版本)
DynamicParam { ## Dynamic DataStore. $DynamicDataStore = 'DataStore' $Attributes = New-Object -TypeName System.Management.Automation.ParameterAttribute $Attributes.Position = 1 $Attributes.Mandatory = $True $AttributeCollection = New-Object -TypeName 'System.Collections.ObjectModel.Collection[System.Attribute]' $AttributeCollection.Add($Attributes) $ValidateScriptAttribute = New-Object -TypeName System.Management.Automation.ValidateScriptAttribute({ If ($PSItem -in (Get-VMHost -Name $VMHost | Get-Datastore).Name) { $True } Else { Throw "Accepted values: $(((Get-VMHost -Name $VMHost | Get-Datastore).Name) -join ', ')" } }) $DynamicArgumentCompleter = New-Object -TypeName System.Management.Automation.ArgumentCompleterAttribute({ Param ($Cmd, $Param, $ParamComplete) [Array]$ValidValues = (Get-VMHost -Name $VMHost | Get-Datastore | Sort-Object FreeSpaceGB -Descending).Name $ValidValues -like "$ParamComplete*" }) $AttributeCollection.Add($ValidateScriptAttribute) $AttributeCollection.Add($DyamicArgumentCompleter) $DynamicParamater = New-Object -TypeName System.Management.Automation.RuntimeDefinedParameter($DynamicDataStore, [String], $AttributeCollection) $DynamicParameterDictionary = New-Object -TypeName System.Management.Automation.RuntimeDefinedParameterDictionary $DynamicParameterDictionary.Add($DynamicDataStore, $DynamicParamater) Return $DynamicParameterDictionary }
问题原因与解决方案
问题原因
- 拼写错误:代码中
$DyamicArgumentCompleter少写了一个n,导致属性未正确添加到集合中。 - 动态参数块处于解析阶段,直接引用
$VMHost会存在作用域问题,无法获取用户已输入的参数值。
修正后的代码
Param( [Parameter( Position = 0, Mandatory = $True, HelpMessage = 'Choose a VM host for the VM(s).' )] [ValidateScript( { If ($null -eq $global:DefaultVIServer) { Write-Host -BackgroundColor Yellow -ForegroundColor Red -Object 'Connect to vCenter!' Break } Else { If ($PSItem -in ((Get-VMHost).Name)) { $True } Else { Throw "Accepted values: $(((Get-VMHost).Name) -join ', ')" } } } )] [ArgumentCompleter( { Param ($Cmd, $Param, $ParamComplete) [Array]$ValidValues = (Get-VMHost | Sort-Object CpuUsageMhz).Name $ValidValues -like "$ParamComplete*" } )] [String]$VMHost ) DynamicParam { $DynamicDataStore = 'DataStore' # 创建基础参数属性 $paramAttr = New-Object System.Management.Automation.ParameterAttribute $paramAttr.Position = 1 $paramAttr.Mandatory = $True # 创建属性集合 $attrCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute] $attrCollection.Add($paramAttr) # 添加ValidateScript校验 $validateScript = New-Object System.Management.Automation.ValidateScriptAttribute({ $selectedHost = $PSCmdlet.GetVariableValue('VMHost') $validStores = (Get-VMHost -Name $selectedHost | Get-Datastore).Name if ($PSItem -in $validStores) { $True } else { Throw "Accepted values: $($validStores -join ', ')" } }) $attrCollection.Add($validateScript) # 添加ArgumentCompleter(修正拼写错误,通过SessionState获取VMHost值) $argCompleter = New-Object System.Management.Automation.ArgumentCompleterAttribute({ Param($Cmd, $Param, $ParamComplete) $selectedHost = $Cmd.SessionState.PSVariable.GetValue('VMHost') if (-not $selectedHost) { return } $validValues = (Get-VMHost -Name $selectedHost | Get-Datastore | Sort-Object FreeSpaceGB -Descending).Name $validValues -like "$ParamComplete*" }) $attrCollection.Add($argCompleter) # 创建动态参数并添加到字典 $dynParam = New-Object System.Management.Automation.RuntimeDefinedParameter($DynamicDataStore, [string], $attrCollection) $dynParamDict = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary $dynParamDict.Add($DynamicDataStore, $dynParam) return $dynParamDict } Begin { $DataStore = $PSBoundParameters[$DynamicDataStore] }
关键修正点
- 修复了
$DyamicArgumentCompleter的拼写错误,确保属性能正确添加到集合。 - 在
ArgumentCompleter脚本块中,通过$Cmd.SessionState.PSVariable.GetValue('VMHost')获取用户已输入的主机参数值,解决作用域问题。 - 在
ValidateScript中使用$PSCmdlet.GetVariableValue('VMHost')替代直接引用$VMHost,保证校验逻辑能正确获取主机值。
内容的提问来源于stack exchange,提问作者Ramil
相关产品推荐
相关产品推荐

