Find the values in ValidateSet

前端 未结 3 1857
时光取名叫无心
时光取名叫无心 2020-12-18 07:58

I was wondering if there was a way to retrieve the values used in the clause Param() for ValidateSet. Something like this would be great:



        
相关标签:
3条回答
  • 2020-12-18 08:25

    validateScript, can provide a more flexible solution and would work well if you needed additional parameter validation. This also allows you to get a list of the valid parameters outside of the foo function, with the creation of the get-validTypes function.

    Function Foo {
        Param (
            [validateScript({test-validTypes $_})]
            [String]$Type = 'Startup'
        )
    
        get-validTypes
    }
    
    function get-validTypes {
    
        $powerOptions = @('Startup', 'Shutdown', 'LogOn', 'LogOff')
        Write-Output $powerOptions
    
    }
    
    function test-validTypes {
    [cmdletbinding()]
    param ($typeInput)
    
        $validTypes = get-validTypes
        if ($validTypes.contains($typeInput)){
            return $true
        } else {
            Write-Error "Invalid Type Paramater, Must be on of the following: $powerOptions"
        }
    
    }
    
    0 讨论(0)
  • 2020-12-18 08:31

    All solutions below work in both functions and scripts.

    Most robust solution that should work in any invocation scenario, PSv2+:

    param (
        [ValidateSet('Startup', 'Shutdown', 'LogOn', 'LogOff')]
        [String]$Type = 'Startup'
    )
    
    ($MyInvocation.MyCommand.Parameters['Type'].Attributes |
      Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] }).ValidValues
    

    A simpler, but fragile PSv3+ solution, which assumes:

    • that Set-StrictMode is either set to -version 1 or not set.

      • Set-StrictMode may have been set outside of your control, so if you don't fully control the execution environment, it is safer to use the more verbose, PSv2-compatible command above.
        (The Set-StrictMode setting behaves like a variable: it is inherited by descendent scopes, but setting it in a descendent scope sets it locally (only affects that scope and its descendants).)

      • However, if you define a function as part of a module, the outside world's Set-StrictMode setting does not apply.

    • that, up to at least Windows PowerShell v5.1 / PowerShell Core v6.0-alpha16, running into this bug when repeatedly dot-sourcing a script is not a concern.

    param (
        [ValidateSet('Startup', 'Shutdown', 'LogOn', 'LogOff')]
        [String]$Type = 'Startup'
    )
    
    (Get-Variable Type).Attributes.ValidValues
    

    Optional background information

    The PSv3+ shorthand syntax (Get-Variable Type).Attributes.ValidValues is essentially the equivalent of:

    (Get-Variable Type).Attributes | ForEach-Object { $_.ValidValues }
    

    That is, PowerShell automatically enumerates the collection .Attributes and collects the values of each element's .ValidValues property.

    In the case at hand, only one attribute in the .Attributes collection - the one of subtype [System.Management.Automation.ValidateSetAttribute] - has a .ValidValues property, so that single value is returned.

    Given that the other attributes have no such property, setting Set-StrictMode to -version 2 or higher causes the attempt to access a nonexistent property to raise an error, and the command fails.

    ((Get-Variable Type).Attributes |
      Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] }).ValidValues
    

    bypasses this problem by explicitly targeting the one attribute of interest (using the -is operator to identify it by type) that is known to have a .ValidValues property.

    The more verbose alternative to accessing the attributes of parameter [variable] $Type with (Get-Variable Type).Attributes is to use $MyInvocation.MyCommand.Parameters['Type'].Attributes.

    Use of the $MyInvocation.MyCommand.Parameters collection enables enumerating and inspecting all parameters without needing to know their names in advance.


    David Brabant's answer is helpful, but (as of this writing):

    • It may create the mistaken impression that separate approaches are needed for scripts and functions.

    • The Get-Command -Name $MyInvocation.MyCommand part is:

      • unnecessary, because $MyInvocation.MyCommand itself provides the information of interest:
        $MyInvocation.MyCommand is an instance of type [System.Management.Automation.ExternalScriptInfo] in scripts, and type [System.Management.Automation.FunctionInfo] in functions, both of which derive from type [System.Management.Automation.CommandInfo], which is the type that Get-Commmand returns - so not only do they provide the same information, they also unambiguously refer to the enclosing script/function.

      • brittle:

        • $MyInvocation.MyCommand is converted to a string due to being passed to the -Name parameter, which in a script results in the script's mere filename (e.g., script.ps1), and in a function in the function's name (e.g., Foo).

        • In a script, this will typically cause Get-Command not to find the script at all - unless that script happens to be in the PATH (one of the directories listed in $env:PATH). But that also means that a different script that happens to have the same filename and that happens to be / come first in the PATH may be matched, yielding incorrect results.
          In short: Get-Command -Name $MyInvocation.MyCommand in scripts will often break, and when it does return a result, it may be for the wrong script.

        • In a function, it can identify the wrong command too, although that is much less likely:
          Due to PowerShell's command precedence, a given name is first interpreted as an alias, and then as a function, so, in theory, with a Foo alias defined, Get-Command -Name $MyInvocation.MyCommand inside function Foo would mistakenly return information about the alias.
          (It's nontrivial to invoke function Foo while alias Foo is defined, but it can be done; e.g.: & (Get-Item Function:Foo))

    0 讨论(0)
  • 2020-12-18 08:39
    function Foo {
        param (
            [ValidateSet('Startup', 'Shutdown', 'LogOn', 'LogOff')]
            [String]$Type = 'Startup'
        )
    
        $ParameterList = (Get-Command -Name $MyInvocation.MyCommand).Parameters
        $ParameterList["Type"].Attributes.ValidValues
    }
    

    After your comment:

    param (
            [ValidateSet('Startup', 'Shutdown', 'LogOn', 'LogOff')]
            [String]$Type = 'Startup'
    )
    
    
    (Get-Variable "Type").Attributes.ValidValues
    

    The Get-Variable call also works in a function.

    0 讨论(0)
提交回复
热议问题