无法在 Validateset 中使用预定义数组 - Powershell

问题描述

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

我正在尝试这样的事情:

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

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

解决方法

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

  • 动态功能需要使用 script block(本身指定为 文字{ ... })或在特定情况下使用 type literal

  • 但是,[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"
}
,

补充@mklement0@Mathias的答案,使用dynamic parameters

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

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

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]

,

除了 mklement0's excellent answer 之外,我觉得有必要指出,在第 5 版及更高版本中,您有一个稍微简单的替代方案:enum

enum 或“枚举类型”是与底层整数值(数字)关联的标签(字符串)的静态列表 - 通过将参数限制为枚举类型,PowerShell 将自动针对它验证输入值并提供参数补全:

enum MyParameterType
{
  Veg
  Fruit
}

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

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

PS ~> Test-ArgumentCompleter -Arg v[<TAB>]
# gives you
PS ~> Test-ArgumentCompleter -Arg Veg
,

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

nil

Register-ArgumentCompleter 的文档在 Microsoft Docs 中有很好的解释。我个人不喜欢使用 $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 ) } 语句,因为它不允许我在智能感知中使用 空格enum 参数与添加描述的功能相同。

输出:

enter image description here

编辑:

@Mklement 在验证提供给参数的参数方面做得很好。如果不使用更多的 powershell 逻辑来为您进行验证(不幸的是,它会在函数体中完成),仅此一项就不允许您这样做。

Validate

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...