如何使argumentcompleter在PowerShell动态参数中工作?

p8ekf7hl  于 2023-06-29  发布在  Shell
关注(0)|答案(1)|浏览(134)

在互联网上有很多例子展示了如何编写一个基本的动态参数,我只见过一个如何在一个函数中有多个动态参数的例子,但我还没有看到任何如何在动态参数中实现参数完成器的例子。
下面是一个基本动态参数的例子:

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]
}

这个实现的问题是,正如您所看到的,我试图首先列出具有最大空间的数据存储,但validateset数组并不关心我的顺序,而是按字母顺序排列值(参数制表符完成在这里工作)。
所以我想到使用argumentcompleter,它确实以我想要的方式在常规参数中工作(参见第一个示例中的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)

    $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
}

这似乎是一个合乎逻辑的方法。
ValidateScriptBlock部件可以工作。我输入了一个不正确的值,并得到一个错误:“接受值:list of comma-delimited values”,但是argumentcompleter不做任何事情。
我期望它做的是当我在-DataStore参数后按tab键时显示接受的值,但是根本没有tab完成,没有按字母顺序排序,或者其他任何东西。

x8diyxa7

x8diyxa71#

最简单和健壮的方法是使用Register-ArgumentCompleter,我不能访问你的完成集,但使用Get-Process作为完成源和一个动态参数,下面是实现的样子:

function Test-DynamicParam {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory, ParameterSetName = 'foo')]
        $foo
    )

    DynamicParam {
        if($PSCmdlet.ParameterSetName -eq 'foo') {
            $paramDictionary = [System.Management.Automation.RuntimeDefinedParameterDictionary]::new()
            $attributes = [Parameter[]] [Parameter]@{
                Position  = 1
                Mandatory = $true
            }
            $runtimeParam = [System.Management.Automation.RuntimeDefinedParameter]::new('bar', [string], $attributes)
            $paramDictionary.Add('bar', $runtimeParam)
            $paramDictionary
        }
    }
}

$params = @{
    CommandName   = 'Test-DynamicParam'
    ParameterName = 'bar'
    ScriptBlock   = {
        param(
            $commandName,
            $parameterName,
            $wordToComplete,
            $commandAst,
            $fakeBoundParameters
        )

        (Get-Process).ProcessName | Sort-Object -Unique |
            Where-Object { $_ -like "*$wordToComplete*" }
    }
}
Register-ArgumentCompleter @params

演示:

相关问题