Search code examples
powershellparameter-passingcommand-line-interface

How to specify switch parameter when calling a script from batch file


I have a script foo.ps1 and a batch file foo.cmd used to launch the script by double clicking the cmd file in file explorer.

The script accepts a switch parameter, but I don't know how to provide this kind of parameter. Simple parameters are ok.

Foo.ps1:

param(
    [Parameter()]
    [Switch]$MySwitch,
    [Parameter()]
    [string]$Name
)

Write-Host "`$MySwitch : $MySwitch, `$Name : $name"

Foo.cmd:

Powershell -noprofile -NonInteractive -file "%~dp0\foo.ps1" -Name "abc"    

If I call the script with only "Name", it works. But If I specify MySwitch, it stops to work:

Foo2.cmd:

Powershell -noprofile -NonInteractive -File "%~dp0\foo.ps1" -Name "abc" -MySwitch:$false

The error is:

C:\temp\foo.ps1 : Impossible de traiter la transformation d'argument sur le paramètre «MySwitch». Impossible de convertir la valeur «System.String» en type « System.Management.Automation.SwitchParameter». Les paramètres booléens acceptent seulement des valeurs booléennes et des nombres, tels que $True, $False, 1 ou 0.
    + CategoryInfo          : InvalidData : (:) [foo.ps1], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : ParameterArgumentTransformationError,foo.ps1

Solution

  • In Windows PowerShell, there is no way to pass Boolean values when powershell.exe's
    -File parameter is used
    - this has has since been corrected in PowerShell (Core) 7+, whose CLI is pwsh.exe[1].

    Use the workaround that JosefZ recommends:

    Use of -Command (-c) instead of -File makes PowerShell treat the arguments as PowerShell source code rather than literal arguments, in which case $false is properly recognized (other CLI parameters omitted for brevity).

    powershell -c "& \"%~dp0\foo.ps1\" -Name 'abc' -MySwitch:$false"  
    

    Note:

    • &, the call operator, must be used to invoke the script file, because its path is quoted.

    • PowerShell expects embedded " chars. on the command line to be escaped as \" (not as `" or "", the way it works inside PowerShell).

      • With -Command, if you need to embed a verbatim " inside an embedded \"...\" string, use `\" (sic).
      • While you could enclose the script path in '...' instead, to avoid the need for escaping, such a call would break if the expanded directory file path (%dp0) happens to contain ' chars. itself.
    • For general guidance on when to use -File vs. -Command and the different syntax rules that apply, see this answer.


    On a conceptual note:

    • Your code already uses the preferred way to pass Boolean arguments in PowerShell: [switch] parameters (sometimes called flags).

    • Switch parameters imply Boolean values: If a given switch - by name only - is specified (-MySwitch), $true is implied; if it is absent altogether, $false is implied. That is, simply omitting -MySwitch:$false would have solved your problem too.

      • By contrast, [bool]-typed parameters - which invariably require an argument - should be avoided. This post contrasts [bool] with [switch] parameters.
    • While (PowerShell-internally) you (always) can pass an explicit value (as you tried with
      -MySwitch:$false, note the required : separator, a space wouldn't work) - doing so is only required in the following scenarios:

      • In case you want to pass a Boolean value via a variable whose value is determined programmatically.

      • In the rare case that a switch defaults to $true, and you need to override that.

        • For conceptual reasons, defaulting switch parameters to $true is best avoided in user code: if necessary, negate the logic of your switch; an example is Write-Host's -NoNewLine switch.

        • However, some of PowerShell's common parameters are switch parameters, which can in effect default to $true via their preference variable counterparts.


    [1] PowerShell (Core) 7+ supports the following Boolean values when -File is used: $true, $false, true, false (and also $null, but its interpretation differs: scripts (including with -File) and functions interpret it as $false, whereas cmdlets interpret it as $true(!)).
    Note that with -Command - and therefore in all PowerShell code - true and false do not work, but 0 and 1 do.
    Unfortunately, if you pass an unsupported value, you get the same error message in all scenarios, which in the -File scenario misleadingly suggests that 0 and 1 work too.