7

我正在寻找一种方法来制作一个接收参数的 cmdlet,在键入时,它会提示从预定义的选项数组中完成建议。

我正在尝试这样的事情:

$vf = @('Veg', 'Fruit')
function Test-ArgumentCompleter {
  [CmdletBinding()]
    param (
          [Parameter(Mandatory=$true)]
          [ValidateSet($vf)]
          $Arg
    )
}

预期的结果应该是:
在编写“Test-ArgumentCompleter F”时,单击 tub 按钮后,F 自动完成为 Fruit。

4

4 回答 4

7
  • PowerShell 通常要求属性属性是文字(例如,'Veg')或常量(例如,$true)。

  • 动态功能需要使用脚本块(本身指定为文字, { ... })或在特定情况下使用类型文字

  • 但是,该[ValidateSet()]属性仅接受字符串数组(按需化)文字或 - 在PowerShell (Core) v6 及更高版本中-类型文字(见下文)。


更新


要基于非文字值数组获得所需的功能,您需要结合其他两个属性:

# The array to use for tab-completion and validation.
[string[]] $vf = 'Veg', 'Fruit'

function Test-ArgumentCompleter {
  [CmdletBinding()]
  param(
    [Parameter(Mandatory)]
    # Tab-complete based on array $vf
    [ArgumentCompleter(
      { param($cmd, $param, $wordToComplete) $vf -like "$wordToComplete*" }
    )]
    # Validate based on array $vf.
    # NOTE: If validation fails, the (default) error message is unhelpful.
    #       Unfortunately, this cannot be helped in *Windows PowerShell*, but in
    #       PowerShell (Core) 7+, you can add an `ErrorMessage` property:
    #         [ValidateScript({ $_ -in $vf }, ErrorMessage = 'Unknown value: {0}')]
    [ValidateScript( { $_ -in $vf })]
    $Arg
  )

  "Arg passed: $Arg"
}
于 2021-05-02T13:46:12.930 回答
7

为了补充来自@mklement0@Mathias的答案,使用动态参数

$vf = 'Veg', 'Fruit'

function Test-ArgumentCompleter {
    [CmdletBinding()]
    param ()
    DynamicParam {
        $RuntimeParameterDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
        $AttributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute]
        $ParameterAttribute = New-Object System.Management.Automation.ParameterAttribute
        $ParameterAttribute.Mandatory = $true
        $AttributeCollection.Add($ParameterAttribute)
        $ValidateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute($vf)
        $AttributeCollection.Add($ValidateSetAttribute)
        $RuntimeParameter = New-Object System.Management.Automation.RuntimeDefinedParameter('Arg', [string], $AttributeCollection)
        $RuntimeParameterDictionary.Add('Arg', $RuntimeParameter)
        return $RuntimeParameterDictionary
    }
}

根据您希望如何预定义参数值,您还可以使用动态 validateSet 值

Class vfValues : System.Management.Automation.IValidateSetValuesGenerator {
    [String[]] GetValidValues() { return 'Veg', 'Fruit' }
}

function Test-ArgumentCompleter {
[CmdletBinding()]
param (
        [Parameter(Mandatory=$true)]
        [ValidateSet([vfValues])]$Arg
    )
}

注意:该类是在 PowerShell 6.0 中引入IValidateSetValuesGenerator[read: interface]

于 2021-05-02T16:39:39.063 回答
6

除了mklement0 的出色回答之外,我觉得有义务指出,在版本 5 及更高版本中,您有一个稍微简单的替代方案可用:enum's

enum或“枚举类型”是与基础整数值(数字)关联的标签(字符串)的静态列表 - 通过将参数约束为枚举类型,PowerShell 将自动验证输入值并提供参数完成:

enum MyParameterType
{
  Veg
  Fruit
}

function Test-ArgumentCompleter {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory=$true)]
        [MyParameterType]$Arg
    )
}

尝试使用制表符完成参数-Arg现在将循环抛出匹配的有效枚举标签MyParameterType

PS ~> Test-ArgumentCompleter -Arg v[<TAB>]
# gives you
PS ~> Test-ArgumentCompleter -Arg Veg
于 2021-05-02T15:30:17.840 回答
3

为了添加其他有用的答案,我为我为工作制作的脚本使用了类似的东西:

$vf = @('Veg', 'Fruit','Apple','orange')

$ScriptBlock = {
    Foreach($v in $vf){
        New-Object -Type System.Management.Automation.CompletionResult -ArgumentList $v, 
            $v, 
            "ParameterValue",
            "This is the description for $v"
    }
}

Register-ArgumentCompleter -CommandName Test-ArgumentCompleter -ParameterName Arg -ScriptBlock $ScriptBlock


function Test-ArgumentCompleter {
[CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        [String]$Arg )
}

Register-ArgumentCompleter的文档在 Microsoft Docs 上有很好的解释。我个人不喜欢使用该enum语句,因为它不允许我在 Intellisense 中使用空格Validate参数与添加描述的好功能相同。

输出:

在此处输入图像描述

编辑:

@Mklement 很好地验证了提供给参数的参数。仅此一项就不允许您在不使用更多的 powershell 逻辑为您进行验证的情况下这样做(不幸的是,它将在函数的主体中完成)。

function Test-ArgumentCompleter {
[CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        $Arg )
      
   if($PSBoundParameters.ContainsKey('Arg')){
       if($VF -contains $PSBoundParameters.Values){ "It work:)" }
           else { "It no work:("}
    }
} 
于 2021-05-02T17:15:43.313 回答