79

I have two PowerShell scripts, which have switch parameters:

compile-tool1.ps1:

[CmdletBinding()]
param(
  [switch]$VHDL2008
)

Write-Host "VHDL-2008 is enabled: $VHDL2008"

compile.ps1:

[CmdletBinding()]
param(
  [switch]$VHDL2008
)

if (-not $VHDL2008)
{ compile-tool1.ps1            }
else
{ compile-tool1.ps1 -VHDL2008  }

How can I pass a switch parameter to another PowerShell script, without writing big if..then..else or case statements?

I don't want to convert the parameter $VHDL2008 of compile-tool1.ps1 to type bool, because, both scripts are front-end scripts (used by users). The latter one is a high-level wrapper for multiple compile-tool*.ps1 scripts.

Martin Brandl
  • 56,134
  • 13
  • 133
  • 172
Paebbels
  • 15,573
  • 13
  • 70
  • 139

5 Answers5

144

You can specify $true or $false on a switch using the colon-syntax:

compile-tool1.ps1 -VHDL2008:$true
compile-tool1.ps1 -VHDL2008:$false

So just pass the actual value:

compile-tool1.ps1 -VHDL2008:$VHDL2008
Martin Brandl
  • 56,134
  • 13
  • 133
  • 172
  • Is the parameter-colon-value syntax new since PS version xx or have I missed this syntax since years? – Paebbels Jun 24 '16 at 08:50
  • 3
    You missed it for years :-). I tested it with 2.0 – Martin Brandl Jun 24 '16 at 08:54
  • Is needs the ispresent mentioned below. Otherwise the parameter is always present (switches don't have values?) – Rob Nicholson Nov 21 '17 at 19:01
  • Just stumbled over this because I had the same problem. I tested it: if you pass -VHDL2008:$false, then $VHDL2008.IsPresent will return $false. – Rolf Sep 22 '18 at 12:12
  • This can be done on the command line too, but you have to arrange for the string```$true```/```$false``` to literally appear on the command line, with dollar sign. – Nathan Schubkegel Jan 14 '19 at 19:07
  • Note: There are a few switch caveats/pitfalls so follow these rules: 1. Never define switch default values. 2. Always use colon: when specifying a switch's value, otherwise it will be passed to the next parameter in the list (Very confusing). https://stackoverflow.com/questions/45691877/why-is-the-value-of-switch-type-parameter-passed-to-a-string-parameter – m1m1k Sep 10 '21 at 15:50
12

Try

compile-tool1.ps1 -VHDL2008:$VHDL2008.IsPresent 
whatever
  • 871
  • 4
  • 11
  • Why should I use the additional `.IsPresent`? – Paebbels Jun 24 '16 at 08:51
  • 1
    Some explanation would be welcome, but it "Gets a value that indicates whether the parameter was specified on the command line. " (from https://msdn.microsoft.com/en-us/library/system.management.automation.switchparameter.ispresent(v=vs.85).aspx) – sodawillow Jun 24 '16 at 08:52
  • ... but it's a switch parameter. As far as I know, it's always defined. If `$VHDL2008` wasn't defined, you would get a NullReference exception, because $null has no method `IsPresent`. Am I right? – Paebbels Jun 24 '16 at 09:01
  • 3
    I've tested this, with `-VHDL2008:$false` or no `-VHDL2008` switch at all, `$VHDL2008.IsPresent` is `$false` : ) (and of course `$true` with `-VHDL2008` or `-VHDL2008:$true`) – sodawillow Jun 24 '16 at 09:36
  • There was some instance where you had to use .isPresent (it may have been passing -verbose or something like that) but is seem to be totally superfluous in this case :) – whatever Jun 24 '16 at 10:22
  • So, `$VHDL2008` is always there, but I could check if is was actually passed in or filled with an implicit default value? – Paebbels Jun 24 '16 at 11:55
  • Yes, iirc it isPresent is false if the switch is not set, and true if it is set- However, it does not have a value $false if it is not set, but a value $true if it is set. You can see this using $PSBoundParameters: `function foo ([switch]$test=$true) { $PSBoundParameters $test }` you can see the difference if you call this function with -test and without – whatever Jun 24 '16 at 12:09
  • Wanted to use the negation of IsPresent and got error about wrong type. Ended up creating a temp variable negating is present and that worked fine. – Alain P Feb 23 '23 at 11:02
1

Assuming you were iterating on development, it is highly likely that at some point you are going to add other switches and parameters to your main script that are going to be passed down to the next called script. Using the previous responses, you would have to go find each call and rewrite the line each time you add a parameter. In such case, you can avoid the overhead by doing the following,

.\compile-tool1.ps1 $($PSBoundParameters.GetEnumerator() | ForEach-Object {"-$($_.Key) $($_.Value)"})

The automatic variable $PSBoundParameters is a hashtable containing the parameters explicitly passed to the script.

Please note that script.ps1 -SomeSwitch is equivalent to script.ps1 -SomeSwitch $true and script.ps1 is equivalent to script.ps1 -SomeSwitch $false. Hence, including the switch set to false is equivalent to not including it.

dacabdi
  • 344
  • 4
  • 14
1

According to a power shell team's blog (link below,) since V2 there is a technique called splatting. Basically, you use the automatic variable @PsBoundParameters to forward all the parameters. Details about splatting and the difference between @ and $ are explained in the Microsoft Docs article (link below.)

Example:

parent.ps1

#Begin of parent.ps1     
param(
    [Switch] $MySwitch
    )

 Import-Module .\child.psm1     

Call-Child @psBoundParameters
#End of parent.ps1

child.psm1

# Begin of child.psm1
function Call-Child {
    param(
        [switch] $MySwitch
    )

    if ($MySwitch){
        Write-Output "`$MySwitch was specified"
    } else {
        Write-Output "`$MySwitch is missing"
    }
}
#End of child.psm1

Now we can call the parent script with or without the switch

PS V:\sof\splatting> .\parent.ps1 
$MySwitch is missing

PS V:\sof\splatting> .\parent.ps1 -MySwitch
$MySwitch was specified

PS V:\sof\splatting> 

Update

In my original answer, I sourced the children instead of importing it as a module. It appears sourcing another script into the original just makes the parent's variables visible to all children so this will also work:

# Begin of child.ps1
function Call-Child {
    if ($MySwitch){
        Write-Output "`$MySwitch was specified"
    } else {
        Write-Output "`$MySwitch is missing"
    }

}
#End of child.ps1

with

#Begin of parent.ps1     
param(
    [Switch] $MySwitch
    )

. .\child.ps1    

 Call-Child # Not even specifying @psBoundParameters   
#End of parent.ps1

Maybe, this is not the best way to make a program, nevertheless, this is the way it works.

About Splatting(Microsoft Docs)

How and Why to Use Splatting (passing [switch] parameters)

Krauss
  • 998
  • 10
  • 17
  • Thanks for adding this solution. What if the parent has more options than the child or where to add / overwrite some parameters if they are different between parent and child? – Paebbels Oct 07 '21 at 19:21
  • If the function if imported as a module, the variables are independent from each other (i.e. If you change the passed parameter in the child function, the corresponding variable in the parent won't be affected.) A different scenario is presented in the case of sourcing where you can consider all variables as global so changing a variable in the child will change the value of the same variable in the parent. – Krauss Oct 14 '21 at 07:34
0

Another solution. If you declare your parameter with a default value of $false:

[switch] $VHDL2008 = $false

Then the following (the -VHDL2008 option with no value) will set $VHDL2008 to $true:

compile-tool1.ps1 -VHDL2008

If instead you omit the -VHDL2008 option, then this forces $VHDL2008 to use the default $false value:

compile-tool1.ps1

These examples are useful when calling a Powershell script from a bat script, as it is tricky to pass a $true/$false bool from bat to Powershell, because the bat will try to convert the bool to a string, resulting in the error:

Cannot process argument transformation on parameter 'VHDL2008'. 
Cannot convert value "System.String" to type "System.Management.Automation.SwitchParameter". 
Boolean parameters accept only Boolean values and numbers, such as $True, $False, 1 or 0.
Polyfun
  • 9,479
  • 4
  • 31
  • 39
  • This answer does not answer my question, which is about calling a powershell script within another powershell script. Default parameters do not work. I'm not using the outdated Windows batch shell... – Paebbels Mar 31 '19 at 00:18