You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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
}

问题原因与解决方案

问题原因

  1. 拼写错误:代码中$DyamicArgumentCompleter少写了一个n,导致属性未正确添加到集合中。
  2. 动态参数块处于解析阶段,直接引用$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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 14:28:08