PowerShell binds in a variety of ways. The pipeline attempts to connect by inspecting the Attributes. of the Function Parameters statement. Explicit binding by name takes precedence whenever the type is compatible.
Here are two variations in behavior:
function Test-MyCmdLetA( [parameter( ValueFromPipeline=$true )]$Name ){ process{ $Name } } # test pipeline for 'function A' dir c:\|Test-MyCmdLetA function Test-MyCmdLetB( [parameter( ValueFromPipeline=$true )][string]$Name ){ process{ $Name } } # test pipeline for function 'B' dir c:\Test-MyCmdLetBIn the first function above notice that the output is the full object even though we have a property called ‘Name’ on the objects returned from the DIR CmdLet. There is NOT sufficient match to bind by property name.
In the second function I have added a ‘type’ declaration or converter to the parameter statement in the form of [string]$name. When we test this function the output is the value of the ‘Name’ property of the objects sent through the pipeline.
Now try this:
function Test-MyCmdLetB( [parameter( ValueFromPipeline=$true )][string]$junk ){ process{ $junk } } # test pipeline for function 'C' dir c:\Test-MyCmdLetCNotice that we still get the name string output. So how is this binding? What if we don’t want the Name string bound to $junk?
How about if we try and fool the code by putting in a second parameter named 'Name’?
function Test-MyCmdLetB( [parameter( ValueFromPipeline=$true )][string]$junk="Not bound", [parameter( ValueFromPipeline=$true )][string]$Name="Not bound" ){ process{ $Name } } dir c:\Test-MyCmdLetBNow we are getting the strings output on the ‘Name’ parameter just like we want but what is happening on the other parameter ‘$junk’?
function Test-MyCmdLetE( [parameter( ValueFromPipeline=$true )][string]$junk="Not bound", [parameter( ValueFromPipeline=$true )][string]$Name="Not bound" ){ process{ Write-Host "Name=$Name and Junk=$junk" } } dir c:\|Test-MyCmdLetEIt appears that we are getting the same output on both arguments…hmmm???
Now try this:
function Test-MyCmdLetF( [parameter( ValueFromPipelineByPropertyName=$true )][string]$junk="Not bound", [parameter( ValueFromPipeline=$true )][string]$Name="Not bound" ){ process{ Write-Host "Name=$Name and Junk=$junk" } } dir c:\Test-MyCmdLetFNow we are only getting the name. $junk is reported as ‘Not bound’. Now rey the following:
function Test-MyCmdLetG( [parameter( ValueFromPipelineByPropertyName=$true )][string]$junk="Not bound", [parameter( ValueFromPipeline=$true )][string]$junk2="Not bound", [parameter( ValueFromPipeline=$true )][string]$Name="Not bound" ){ process{ Write-Host "Name=$Name and Junk=$junk and Junk2=$junk2" } } dir c:\Test-MyCmdLetGNow only the property called ‘Name’ is being bound. Why is the first argument being bound even though the named argument (parameter) is being bound? What is really happening here?
This is the way the properties are discovered and bound. IN the absence of anexplicit request by name the properties are bound and discovered in order applying any type conversions possible.
Here is another bit that will help in understanding what is going on.
function Test-MyCmdLetB( [parameter( ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true )][string]$junk="Not bound", [parameter( ValueFromPipeline=$true )][string]$junk2="Not bound", [parameter( ValueFromPipeline=$true )][string]$Name="Not bound" ){ process{ Write-Host "Name=$Name and Junk=$junk and Junk2=$junk2" } } dir c:\Test-MyCmdLetBI have added the ValueFromPipeline statement in along with the ValueFromPipelineByPropertyName statement and now the output, once again, binds to ALL parameters. Adding both of these statements to the same parameter causes the behavior to ignore the value by name version. Use only one of these in any parameter.
Here are some references to articles that will help to clarify what is happening and why PowerShell s designed this way:
PowerShell Team Blog: ValuFromPipeLineByPropertyName
Microsoft Connect:ValueFromPipelineByPropertyName overridden by ValueFromPipeline
Previous:
Script CmdLets
The Pipeline
Next:
Hey this is super old, but I thought it was a really interesting post. The last few examples have a typo, should say Junk2=$junk2
ReplyDeleteThank you - I think I have fixed all of the typos and omissions.
DeleteGlad you liked the post.