Repository navigation
Description
Prerequisites
- Existing Issue: Search the existing issues for this repository. If there is an issue that fits your needs do not file a new one. Subscribe, react, or comment on that issue instead.
- Descriptive Title: Write the title for this issue as a short synopsis. If possible, provide context. For example, "Typo in
Get-Foocmdlet" instead of "Typo." - Verify Version: If there is a mismatch between documentation and the behavior on your system, ensure that the version you are using is the same as the documentation. Check this box if they match or the issue you are reporting is not version specific.
Links
https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_functions_advanced_parameters#switch-parameters
https://learn.microsoft.com/en-us/powershell/scripting/developer/cmdlet/types-of-cmdlet-parameters#switch-parameters
Summary
It's possible to pass an explicit boolean value to a switch parameter, either with hash table splatting or -Param:$bool syntax.
The implications of this (specifically, with an explicit $false value) are not currently documented, despite requiring consideration by cmdlet and function authors alike.
New-PSSession is an example of a command that does not take the implications into consideration, resulting in unexpected behavior for the user.
# The *presence* of the switch selects UseWindowsPowerShellParameterSet.
# Irrespective of the switch *value*, CreateRunspacesForUseWindowsPowerShellParameterSet() is called.
# Most PS users would not expect this to create a Windows PS session, but that's precisely what happens.
New-PSSession -UseWindowsPowerShell:$falseDetails
-
Parameter sets are tied to the presence of the switch, not the value. Modifying command behavior based on the parameter set is problematic if a switch controls which parameter set is chosen.
function Test { param ([Parameter(ParameterSetName = 'SwitchParam')] [switch] $SwitchParam) switch ($PSCmdlet.ParameterSetName) { SwitchParam { 'Switch behavior' } default { 'Default behavior' } } } Test # Default behavior Test -SwitchParam:$true # Switch behavior Test -SwitchParam:$false # Switch behavior
For real-world examples where this is problematic, see:
-
As of v7.6.0, there are at least 10 commands shipped with PowerShell that ignore an explicit
$falseswitch value due to using the above practice. See Treat-Switch:$falsethe same as omitting a switch when selecting a parameter set PowerShell/PowerShell#25027 (comment) for details. These commands subvert reasonable user expectation when the switch is passed an explicit$falsevalue.Get-Random -ShuffleGet-SecureRandom -ShuffleGet-TimeZone -ListAvailableNew-PSSession -UseWindowsPowerShellRegister-PSResourceRepository -PSGallerySplit-Path -Qualifier/-NoQualifier/-Leaf/-IsAbsolute(not-Extension/-LeafBase)Test-Connection -Repeat/-MtuSize/-TracerouteWhere-Object(all non-common switch parameters)Get-Uptime -SinceNew-Guid -Empty
-
Using
InvocationInfo.BoundParameters/$PSBoundParameterswithout checking both the key and value is similarly problematic and should generally be avoided. E.g.,ContainsKey()reflects the presence of the switch, irrespective of it being passed an explicit value.function Test { param ([switch] $SwitchParam) if ($PSBoundParameters.ContainsKey('SwitchParam')) { 'Switch behavior' } } Test -SwitchParam:$false # Switch behavior
-
The natural alternative to using switch-controlled parameter sets is to instead use a single, enum/set-based parameter and branch off that instead (though of course this is not always a viable option). For example:
# Two separate switches with three separate parameter sets # Breaks down with splatting and -Param:$bool syntax. function SwitchBased { [CmdletBinding(DefaultParameterSetName = 'Timespan')] param ( [Parameter(ParameterSetName = 'Since')] [switch] $Since, [Parameter(ParameterSetName = 'Pretty')] [switch] $Pretty ) switch ($PSCmdlet.ParameterSetName) { Timespan { 'Timespan'; break } Since { 'Since'; break } Pretty { 'Pretty'; break } } } # One set-based parameter. # Not susceptible to the issues above. function SetBased { [CmdletBinding()] param ( [ValidateSet('Timespan', 'Since', 'Pretty')] [PSDefaultValue(Help = 'Control how output is formatted.')] [string] $Format = 'Timespan' ) switch ($Format) { Timespan { 'Timespan'; break } Since { 'Since'; break } Pretty { 'Pretty'; break } } }
Note: The proposal in Treat
-Switch:$falsethe same as omitting a switch when selecting a parameter set PowerShell/PowerShell#25027 seeks to address the outlined parameter set issue systematically.
Activity