powershellvariablesswitch-parameterparameter-splatting

How to pass a switch parameter as a variable / via splatting in PowerShell?


If you have multiple parameters which require a value when calling a command or a script, I know you can pass it like this:

$parameters = @{
    name = "John"
    last_name = "Doe"
}

But if the command or script actually just expect -T to indicate something like a flag, but the parameter itself doesn't require a value. How can I set that in a variable?

$optionalT = ""
if ($itNeedsTheT) $optionalT = "-T"

command $optionalT

If I do it like that it complains with the following message:

Unknown argument 'T' on command line.

Solution

  • tl;dr

    # Pass the $itNeedsTheT Boolean - which indicates whether the -T switch should
    # be passed - as the switch's *value*.
    command -T:$itNeedsTheT  
    

    If $itNeedsTheT is $false, the above is the same as omitting -T - usually (read on for details).

    Note the need to use : to separate the switch name from the value.


    As boxdog points out in a comment, in a hashtable used with splatting (@parameters), you use a Boolean value to represent a switch parameter (a flag-like parameter of type [switch]) .

    # Dynamically determine if -Recurse should be turned on.
    $recurseIfTrue = $true
    
    # Define the hashtable for splatting...
    $parameters = @{
      Path = '.'
      Recurse = $recurseIfTrue  # turn the -Recurse switch on or off
    }
    
    # ... and pass it to the target command.
    # *Loosely speaking*, the following command is the same as either:
    #   Get-ChildItem -Path '.' -Recurse  # if $recuseIfTrue was $true
    # or:
    #   Get-ChildItem -Path '.'           # if $recuseIfTrue was $false
    Get-ChildItem @parameters
    

    That is, loosely speaking:

    This allows you to keep a single hashtable definition that unconditionally includes the switch parameter, but whose value can determine programmaticaly.

    Caveat:

    Strictly speaking, hashtable entry Recurse = $true translates to parameter -Recurse:$true and Recurse = $false doesn't translate into omitting the parameter, it translates to passing -Recurse:$false.

    In most cases, omitting a switch -Foo and passing it with value $false - i.e. -Foo:$false - are equivalent.

    However, commands can detect the difference and sometimes act differently:

    A notable example is the -Confirm common (switch) parameter: omitting -Confirm means that the $ConfirmPreference preference variable is respected, whereas -Confirm:$false means that the preference variable should be overridden (and confirmation should not be requested).

    If you want to make this distinction yourself in a PowerShell script or function, you can call $PSBoundParameters.ContainsKey('Foo') in addition to checking the $Foo (-Foo) switch parameter variable's value.

    If you're dealing with such a command and you want to programmatically enforce omission of a switch parameter, you'll have no choice but to conditionally add an entry for this switch, in a separate step:

    # Dynamically determine if -Recurse should be turned on.
    $recurseIfTrue = $true
    
    # A 'Recurse' key now can NOT be included unconditionally,
    # if you want to *omit* -Recurse in case $recurseIfTrue is $false
    $parameters = @{
      Path = '.'
    }
    
    # Add a 'Recurse' entry only if the switch should be passed.
    if ($recurseIfTrue) {
      $parameters.Recurse = $true
    }
    
    Get-ChildItem @parameters
    

    Finally, note that as an alternative to specifying a switch value programmatically via splatting, you can pass a dynamic value to a switch directly:

    # Dynamically determine if -Recurse should be turned on.
    $recurseIfTrue = $true
    
    Get-ChildItem -Path . -Recurse:$recurseIfTrue
    

    Note the need to use : to separate the switch name from its value.

    This is necessary, because using the customary whitespace to separate the parameter name from its value would cause PowerShell to interpret the Boolean as the next argument, given that switch parameters normally do not take values.

    Although rarely used, this :-based syntax works with all parameter types.