In item 11, I covered ByPropertyName pipeline bound parameters. In this post, I’ll cover the other variety of pipeline binding – ByValue. ByValue binding takes the input object itself and attempts to bind it by type using type coercion if possible to parameters decorated as ByValue. For example, most of the *-Object utility cmdlets operate on whatever object is presented to them. The help on Where-Object shows this:
-inputObject <psobject>
Specifies the objects to be filtered. If you save the output of a command in a variable,
you can use InputObject to pass the variable to Where-Object. However, typically, the
InputObject parameter is not typed in the command. Instead, when you pass an object
through the pipeline, Windows PowerShell associates the passed object with the
InputObject parameter.
Required? false
Position? named
Default value
Accept pipeline input? true (ByValue)
Accept wildcard characters? false
It turns out that ByValue isn’t nearly as popular as ByPropertyValue. How can I make such a statement you ask? Well this is one of the things that I love about PowerShell. It provides so much metadata about itself. It is very "self describing". You can easily walk every parameter on every cmdlet that is currently loaded into PowerShell. First let’s see what information is available for a parameter:
PS> Get-Command -CommandType cmdlet | Select -Expand ParameterSets | |
The interesting properties for us here are the Name and ValueFromPipeline* properties. Given this information it is easy to figure out how many of each type there are:
PS> (Get-Command -CommandType cmdlet | Select -Expand ParameterSets | Select -Expand Parameters | |
So from here we can see the following:
Type of Pipeline Binding | Count |
ValueFromPipeline | 55 |
ValueFromPipelineByPropertyName | 196 |
Both | 66 |
So indeed binding by property name is much more common. Binding by value from the pipeline is primarily for cmdlets that manipulate objects. In the query below we can see that the InputObject parameter is by far the most common "ByValue" pipeline bound parameter:
PS> Get-Command -CommandType cmdlet | Select -Expand ParameterSets | Select -Expand Parameters | |
A little further digging reveals the cmdlets that use the ByValue bound InputObject parameters as shown below. Note that a single parameter can appear in more than one parameter set on a cmdlet, which explains why there are only 36 cmdlets that account for the 40 instances of InputObject.
PS> $CmdletName = @{Name='CmdletName';Expression={$_.Name}} PS> Get-Command -CommandType cmdlet | Select $CmdletName -Expand ParameterSets | |
As you can see most of these cmdlets are designed to deal with objects in general. Note to cmdlet developers – pipeline bound parameters is how your cmdlets receive pipeline objects. When writing a cmdlet there is no $_. If your cmdlet wants to "participate" in the pipeline it must set the ParameterAttribute property ValueFromPipeline and/or ValueFromPipelineByPropertyName to true on at least one of its parameters.
As mentioned above most ByValue parameters are of the InputObject (type psobject or psobject[]) variety so they pretty much accept anything. However not all cmdlets work that way. The -Id parameter (type [long[]]) on Get-History is pipeline bound ByValue. The follow Trace-Command output shows how PowerShell works hard when necessary to convert the input object’s type to the expected type. In this case a scalar string value of ‘1’ to an array of Int64:
PS> Trace-Command -Name ParameterBinding -PSHost -Expression {'1' | get-history} |
Note that on the first attempt, PowerShell tries to convert the string to an array of Int64 and fails. Then it tries again by treating the input as psobject. It throws that psobject at an internal help class method LanguagePrimitives.ConvertTo() that successfully converts the string ‘1’ to an Int64[] containing the value 1.
When a parameter is both ByValue and ByPropertyName bound, PowerShell attempts to bind in this order:
- Bind ByValue with no type conversion
- Bind ByPropertyName with no type conversion
- Bind ByValue with type conversion
- Bind ByPropertyName with type conversion
There is more to the parameter binding algorithm like finding the best match amongst different parameter sets. BTW one last tidbit related to parameters. The PowerShell help topics aren’t completely automatically generated and as a result they aren’t always correct. For instance, look up the parameters on Get-Content and see if you find a -Wait parameter – you won’t. 🙂 However the metadata is always complete and correct e.g.:
PS> Get-Command Get-Content -Syntax Get-Content [-Path] <String[]> [-ReadCount <Int64>] [-TotalCount <Int64>] [-Filter <String>] |
Hopefully this post has given you more knowledge about ByValue parameters and how to explore and get more information on cmdlet parameters in general. In summary, there actually isn’t much you need to know about ByValue pipeline bound parameters because in most cases they just work intuitively. Just be sure to keep your eye out for those parameters that bind ByPropertyName. They are the ones whose pipeline bound usage isn’t as obvious.
Great and concise.
In the penultimate paragraph you mentioned: “There is more to the parameter binding algorithm like finding the best match amongst different parameter sets.” Could you point me to a place dealing with this topic?
Thanks a lot
The only place I’m aware that has parameter binding information is Bruce Payette’s Windows PowerShell in Action 2nd Edition. He covers the parameter binder in chapters two and three.
The link in this post is broken for part 11. This link appears to work:
Thanks for the heads up. Fixed.
Your code is white foreground on a light grey background. It’s impossible to read.
Unfortunately my site was migrated live spaces to wordpress and in the process many of my old post’s code formatting got destroyed. Grab the PDF version of all the effective posts here https://onedrive.live.com/view.aspx?cid=5A8D2641E0963A97&resid=5A8D2641E0963A97%216929&app=WordPdf