How to mark a switch parameter as mandatory in Powershell - powershell

I'm pretty sure I don't have any other options other than what I've uncovered, but I wanted to pick the collective internet brain.
I prefer to use a [switch] parameter when passing boolean values to custom functions. However, I have a few cases in which I wanted to mark the switch parameter as mandatory. This can optionally be accomplished through [parameter(Mandatory = $true)] on the parameter. However, I really dislike the UI prompt that shows up. I much prefer throwing an exception.
But, a switch can be either true or false, and the "IsPresent" property makes no distinction. If I pass a switch parameter as -example:$false, the switch reports that $example.IsPresent is false!
I have resorted to using a [bool]. For example:
param
(
[bool]$theValue = $(throw('You didn't specify the value!'))
);
Are there any other tricks I can pull?

In a way a switch parameter is always mandatory. If you don't specify it, it gets a value of false. If you specify it ( -var) it gets a value true and if you specify the value too (-var:$false) it gets the value specified.
I can't really think of a situation where it is mandatory to specify a switch. If you don't specify, it is false. Simple as that.
I think what you want is to specifically mention the value of the param to be true or false? If that is the case, the bool version that you mention is what I would go for, though it works the same way with switch too:
param([switch]$a = $(throw "You didn't specify the value"))
And also regarding $example.IsPresent - I know it is not intuitive /broken , but it is the same as the value of the switch variable itself. That is how the constructor for Switch Paramater is defined and the only property that it has is the IsPresent:
Creates a new SwitchParameter object that includes a Boolean value
that identifies whether the switch is present.
http://msdn.microsoft.com/en-us/library/system.management.automation.switchparameter.ctor%28v=vs.85%29.aspx

Related

Single parameter argument with position equal to 1 or no argument value?

I have stumbled upon a function with single parameter, and there are 2 things I do not understand simply because documentation does not mention them.
Here is a function:
function Some-Function
{
[CmdletBinding()]
param (
[Parameter(Mandatory,
Position = 1, ValueFromPipeline)]
[string] $Input
)
}
Question 1:
Since this is single parameter function, why is Position set to 1? what does that mean? I don't see any point here, what is wrong with the default value of Position = 0?
Question 2:
ValueFromPipeline and Mandatory arguments are not set to any value ie, = $true or = $false
what is the default value of these arguments if not set to value?
I can't find anywhere in the docs to explain this, and none of the examples on docs are like this one.
Since this is single parameter function, why is Position set to 1?
There is no strict need to set a Position property at all in this case.
While it is a good convention to start Position properties with 0, it isn't technically necessary - all that matters is the relative ordering of all parameters with Position attributes.
In the absence of any Position properties, it is the order of parameter declarations (other than [switch] parameters) that implicitly defines their positional ordering, EXCEPT if you use [CmdletBinding(PositionalBinding=$false)], in which case only explicit Position properties matter.
Note: The presence of [CmdletBinding(PositionalBinding=$false)] or at least one [Parameter(Position=...)] attribute implicitly switch the default to non-positional: that is, all parameters that do not explicitly specify a Position value become non-positional, meaning they can only be passed arguments if preceded by the parameter's name (e.g., -foo bar instead of just bar).
ValueFromPipeline and Mandatory arguments are not set to any value
In PowerShell v3+, omitting an attribute's property value defaults to $true, in the interest of brevity.

Is there a PowerShell equivalent for C#'s default operator, and if so what is the syntax?

In C#, it's possible to get the default value of any type using the default operator:
var i = default(int); // i == 0
// in C# 7.1+
int j = default; // j == 0
Is there a similar construct in PowerShell, and if so what is it? As far as I've been able to determine in my Googling and testing, default is only recognized by PS when present in switch blocks.
PowerShell has no direct language construct for it because it doesn't need it -- due to its loose typing you are almost never required to produce a value of a specific type and there is no support for creating generic types or functions. Untyped variables start off as $null if you do nothing special. Typed variables start off as whatever value you explicitly give them, and that's generally sufficient due to PowerShell's liberal rules for conversion ([int] "" and [int] $null are both 0).
Only in rare cases does this fail, like attempting to declare a variable of type DateTimeOffset, as there is no default constructor and $null or "" won't convert. Arguably, the fix there is to just explicitly construct a value using whatever the type does offer ([DateTimeOffset] $d = [DateTimeOffset]::Now, [DateTimeOffset] $d = [DateTimeOffset]::MinValue, [DateTimeOffset] $d = "0001-01-01 00:00Z"). Only in the very rare case that you have a dynamic type, and you'd like to get what C# would give you with default, would you need some special code. You can do it in pure PowerShell (well, almost, we need to call a method available since .NET 1.0):
Function Get-Default([Type] $t) { [Array]::CreateInstance($t, 1)[0] }
And then [DateTimeOffset] $d = Get-Default DateTimeOffset works (there is no way to infer the type in this case, though you are of course free to omit it from the variable).
Of course this does create a garbage array on every invocation; it does not invoke any constructors of the type itself, however. There are more involved approaches that avoid array creation, but they all involve getting complicated with generic methods (relying on LINQ) or explicitly compiling C# and aren't really worth demonstrating as they're less general. Obviously, even the function above should be used only in the unusual case where it might be needed and not as a general way of initializing variables -- typically you know the type and how to initialize it, or you don't care about the type in the first place.

Is there some way to define a parameterless powershell parameter set?

I've got some fairly complex functions that I'm writing for a library module, with lots of different ways it can be called. However, it is in fact possible to default all of them, but when I try to call my function with no parameters the call fails because the parameter set cannot be determined.
I would like to define a parameter set that contains no parameters whatsoever, such that calls with no parameters succeed. This is difficult to do since ParameterSetName is a property of the Parameter attribute though, and it's not possible to attribute nothing.
I experimented with the DefaultParameterSet property of the CmdletBinding attribute that is placed on the param block, however nothing seemed to work. It seems that the parameter set name defined there must actually exist in order for Powershell to default to it.
Currently my best approximation of this use case is to define one of the parameter sets to have no Mandatory parameters, however these fail when empty strings or nulls are piped in, and I would like for this not to be the case.
Is this possible?
Sure is. Just specify a default parameter set name that isn't used otherwise:
function Foo {
[CmdletBinding(DefaultParameterSetName='x')]
Param(
[Parameter(Mandatory=$true, ParameterSetName='y')]$a,
[Parameter(Mandatory=$false, ParameterSetName='y')]$b,
[Parameter(Mandatory=$true, ParameterSetName='z')]$c,
[Parameter(Mandatory=$false)]$d
)
"`$a: $a"
"`$b: $b"
"`$c: $c"
"`$d: $d"
}

How does ValidateSet stop a variable being changed to a non-set value?

I've just noticed that if you are using ValidateSet for a parameter variable in a function you cannot within that function change the parameter variable to a value that is not in the set.
Here is a simple example to demonstrate:
Function Test {
[cmdletbinding()]
Param(
[ValidateSet(1,2,3)]
[int]$Number
)
$Number = 4
}
Test 3
Returns:
The variable cannot be validated because the value 4 is not a valid value for the Number variable.
I've used Get-Member to explore $Number and I can't see any indication of how (or why) it restricts the variable like this. I assume it's some sort of custom object or strong typing but the variable looks to be a System.Int32. Does anyone know how/why this happens?
This isn't unique to Int variables, this is just a simple example. I have found the same true for a String Array parameter.
I discovered from this related question: Find the values in ValidateSet that if I do the following inside my function:
(Get-Variable 'Number').Attributes.ValidValues
this lists the defined ValidateSet values. I therefore assume setting this attribute is how ValidateSet works, with the side effect being that it is then in effect throughout the life of the variable.

not using [parameter()] on some parameters in a PowerShell advanced function

If you are doing advanced functions and have the parameters decorated [parameter()], would there be any reason to NOT decorate a parameter with [Parameter()] . I've seen this a few times and don't know whether its just a case of laziness, oversight, or purposeful design.
Laziness I think but to be fair, it isn't needed if you aren't using any special parameter attibutes like Mandatory, Position, etc. As long is one parameter is marked [Parameter(...)] or the param block is marked [CmdletBinding(...)] PowerShell will interpret that function as an advanced function. OTOH there is no harm in adding the empty [Parameter()] to parameters.