$PSBoundParameters vs $PSCmdlet.MyInvocation.BoundParameters

by gbritton at 2013-05-01 06:43:08

I’m seeing two ways to access the parameters passed to my script:

$PSBoundParameters and $PSCmdlet.MyInvocation.BoundParameters

I’m trying to understand the difference between the two. In a little test I did, it looks like they are equivalent. e.g.


Write-Host $PSCmdlet.MyInvocation.BoundParameters.pstypenames
Write-Host $PSBoundParameters.pstypenames


produces:

[quote]
System.Collections.Generic.Dictionary2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Object, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]] System.Object<br>System.Collections.Generic.Dictionary2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Object, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]] System.Object
[/quote]

which appear to be identical to me. (Actually, I was looking for an equivalent to Python’s "is" operator, which returns True if two objects are indeed identical, that is, if they both point to the same RAM location, since everything in Python is a pointer. Does such a thing exist for PowerShell?)

So, are these two identical? Are they both always present? If not, what are the conditions? I got curious because this line


if ($PSCmdlet.MyInvocation.BoundParameters["Debug"].IsPresent) {<do something>}


sometimes gives me this error:

[quote]
Cannot index into a null array.
At line:1 char:44
+ if ($PSCmdlet.MyInvocation.BoundParameters[ <<<< "Debug"].IsPresent -or
+ CategoryInfo : InvalidOperation: (Debug:String) , RuntimeException
+ FullyQualifiedErrorId : NullArray
[/quote]
by DonJ at 2013-05-04 08:55:11
The correct way to access the parameters passed to your script is to define a Param() block and define each parameter. Accessing parameters through either of these collections isn’t the intended mode.

PowerShell doesn’t manage memory or variables quite that way. There’s no native operator to test if two variables are pointers to the same memory location - .NET itself doesn’t conceptualize things quite that way, and PowerShell abstracts things a lot more than even .NET.

To get back to the point, I’d never need to test for the presence of the debug parameter. Assuming you’re using the default one added by [CmdletBinding()], -Default is a [switch] $Debug would be $True or $False automatically. But if you did want to check, $PSBoundParameters.ContainsKey("myparam") would be the preferred syntax. PowerShell also doesn’t use named indexes in arrays quite like you’re attempting.
by gbritton at 2013-05-06 06:23:35
[quote]
The correct way to access the parameters passed to your script is to define a Param() block and define each parameter. Accessing parameters through either of these collections isn’t the intended mode.
[/quote]

Yes, though I just wanted the common parameters available through [CmdletBInding()]

[quote]
$PSBoundParameters.ContainsKey("myparam") would be the preferred syntax
[/quote]

Now, that’s interesting! When googling this very topic so far, most of the hits (actually all of the hits until today) said do it this way:


if ($PSCmdlet.MyInvocation.BoundParameters["Debug"].IsPresent) {<do something>}


e.g.
http://msdn.microsoft.com/en-us/library … nt(v=vs.85).aspx
http://stackoverflow.com/questions/4301 … tom-cmdlet
http://mnaoumov.wordpress.com/2012/09/2 … ur-scripts

So, I put together a little test case:


[CmdletBinding()]
param()

if ($PSBoundParameters.ContainsKey(‘Debug’)) {
"$Debug specified, value = $($PSBoundParameters&#91;'Debug'&#93;)&quot;<br>} else {<br> &quot;$Debug not specified"
}

if ($PSCmdlet.MyInvocation.BoundParameters["Debug"].IsPresent) {
"$Debug specified, value = $($PSBoundParameters&#91;'Debug'&#93;)&quot;<br>} else {<br> &quot;$Debug not specified"
}


If I save this and invoke it, I get the same results from both tests, with or without the debug switch set:


C:\dt11258\ps1>powershell -File .\testdebug2.ps1
$Debug not specified
$Debug not specified

C:\dt11258\ps1>powershell -File .\testdebug2.ps1 -debug
$Debug specified, value = True
$Debug specified, value = True


Base on this, is it safe to say that either test is valid? If so, I’d prefer the first one, simply because its shorter.

FWIW I need to test for the debug option in some code under development, since the intention is that it will write to production files once promoted. In the meantime, I use the -Debug switch to control where it writes.

Unless there is a better way from Powershell best practices? I’m still relatively new at this.
by DonJ at 2013-05-06 07:32:08
You might just consider defining your own [switch]. The -Debug switch should control Write-Debug’s output - because it’s a common parameter with a defined behavior, you shouldn’t hijack it to do something else. Controlling where something writes prior to going into production is a great idea - just -Debug isn’t the right switch.