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:
Function Foo {
Param (
[ValidateSet('Startup', 'Shutdown', 'LogOn', 'LogOff')]
[String]$Type = 'Startup'
)
$Type.ValidateSet
}
But of course there is no such property on the Type
object. Is it possible to retrieve the values set in ValidateSet
?
The ValidateSetAttribute attribute specifies a set of possible values for a cmdlet parameter argument. This attribute can also be used by Windows PowerShell functions.
By default, the ValidateSet attribute is case insensitive. This means that it will allow any string granted it's in the allowed list with any capitalization scheme.
PowerShell uses parameter sets to enable you to write a single function that can do different actions for different scenarios. Parameter sets enable you to expose different parameters to the user. And, to return different information based on the parameters specified by the user.
These parameters are added at runtime and are referred to as dynamic parameters because they're only added when needed. For example, you can design a cmdlet that adds several parameters only when a specific switch parameter is specified. Note. Providers and PowerShell functions can also define dynamic parameters.
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.
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
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)
)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With