We know we can pass objects down the PowerShell pipeline, but how do we know which parameter accepts objects from the pipeline and how it binds them? Understanding parameter binding in the PowerShell pipeline is essential if you want to create your own tools.

The pipeline in PowerShell allows you to connect objects by the | pipe operator. The pipeline will take the output from a command and pass it to the next command using the pipe operator. PowerShell allows you to pipe through many commands, not restricting you to a limit. This is a powerful and elegant feature to work with. Consider this—you can find a running service and pipe it straight into a stop command:

Get-Service -Name 'Bits' | Stop-Service

There's no extra work, and it keeps your code concise. But have you wondered how this works? How do we know Stop-Service will allow us to stop the service? After all, it could just fail. Which parameter on Stop-Service takes pipeline input? PowerShell provides all the tools we need to get this information right from the console.

ValueFromPipeline and ValueFromPipelineByPropertyName ^

Before talking about parameter binding, it is useful to know the techniques PowerShell uses for pipeline integration. It executes each pipeline object once within the process block. Using the ValueFromPipeline property allows acceptance of values by parameters from the pipeline. With ValueFromPipeline, you can bind the whole object passed to the parameter. Conversely, ValueFromPipelineByPropertyName uses a property on the object instead of the entire object. The name of the property used is from the parameter name itself.

Parameter binding ^

The pipeline prepares two lists of unbound pipeline parameters, one called ValueFromPipeline and a second called ValueFromPipelineByPropertyName, as mentioned. It organizes these lists using ParameterSets. Each list compiles pipeline parameters, taking input from either ValueFromPipeline or ValueFromPipelineByPropertyName. ValueFromPipeline and ValueFromPipelineByPropertyName are property attributes set on the cmdlet, function, or script parameters with a value of True. It puts the default parameters in a ParameterSet at the top of the lists and uses them for binding first.

By using the Get-Help cmdlet, we can see which parameters of a cmdlet, function, or script take values from the pipeline. Here I'm using the Get-ChildItem cmdlet:

Get-Help 'Get-ChildItem' -Parameter * | Where-Object pipelineInput -Match '^true'
Get Help showing which parameters accept pipeline values

Get Help showing which parameters accept pipeline values

From the output, we can see Path and LiteralPath accept pipeline input. We get some useful information back. For Path we can use object inputs of ByValue and ByPropertyName. We also see which ParameterSet this is part of and the value type of string.

Get-Command ^

It is also possible to check parameters that accept pipeline input from Get-Command as well.

(Get-Command Get-Process).ParameterSets.Where{
    $_.ParameterType -ne [Switch] -and ($_.ValueFromPipeline -or $_.ValueFromPipelineByPropertyName)

The process through Get-Command is a little more complicated but does return a richer set of objects to evaluate.

Get Command pipeline parameters

Get Command pipeline parameters

Tracing the parameter-binding process ^

I'm going to use the Trace-Command cmdlet now to show how this is working under the hood.

By piping the root of C:\ with quotes to indicate a string, we get a directory listing:

'C:\' | Get-ChildItem

By adding the code inside the expression script block parameter on Trace-Command, we can trace what is happening. The Name parameter of Trace-Command accepts many different values, so it's worth exploring. For what I am demonstrating, ParameterBinding is perfect.

NOTE: I've suppressed the output of Get-ChildItem in the Trace-Command expression since I'm not interested in the output of the command, just the parameter binding process.

Trace-Command -Name ParameterBinding -Expression { $null = 'C:\' | Get-ChildItem } -PSHost
Using Trace Command to show Get ChildItem parameter bindings

Using Trace Command to show Get ChildItem parameter bindings

What does all this output mean? I'll focus from CALLING BeginProcessing:

PIPELINE object TYPE = [System.String]

The pipeline has quickly found out the object we are passing is of type String.

Parameter [Path] PIPELINE INPUT ValueFromPipeline NO COERCION

It has found the match to the Path parameter, stating 'NO COERCION'.

NOTE: COERCION is when you take an object of one type and convert its value to another type when performing an operation or evaluation.

In this example of parameter binding, this is not necessary.

BIND arg [C:\] to parameter [Path]

Here the binding indicates we passed the string of 'C:\' as an argument, matching it to the Path parameter.

Binding collection parameter Path: argument type [String], parameter type [System.String[]], collection type Array, element type [System.String], no coerceElementType

This confirms the argument we passed is of type String and the Path parameter type is also a String. Also, it's worth noting here the Path parameter takes an array, [System.String[]].

Creating array with element type [System.String] and 1 elements

This line confirms the parameter binding is creating an array to pass through the pipeline.

BIND arg [System.String[]] to param [Path] SUCCESSFUL

The parameter binding of our string object to the Path parameter is successful. This is a nice simple example to start looking at; more complicated pipelines can take longer to debug!

Parameter binding process ^

This brings us to how the binding process works now that we've seen a small part of it in action with Trace-Command.

The command manages the binding process. Parameters as we now know have attributes that let us bind them from the pipeline. This is how the binding process goes:

Subscribe to 4sysops newsletter!

  1. First, it binds all named parameters. It attempts to convert the type of argument passed through the pipeline to the matching parameter to bind.
  2. PowerShell parameters can have other features, such as being mandatory and positional. This is the next step of the binding process, to bind all positional parameters. It attempts to bind unbound parameters that are positional.
  3. It then attempts to bind any parameters still unbound and of value (not named) to a parameter of the same match.
  4. If still not bound to a parameter at this stage, it tries to bind the Value argument by type conversion.
  5. It attempts step 3, but instead of binding by value, binding by name takes place.
  6. Finally, it attempts step 4, but as with the last step, by name and not by value.

Summary ^

Parameter binding can be more complicated than first thought, and as this article tries to show, a lot is going on under the hood. But as you create your own advanced functions, knowing how the process works will be invaluable to building great and flexible tools.


Leave a reply

Please enclose code in pre tags

Your email address will not be published. Required fields are marked *


© 4sysops 2006 - 2022


Please ask IT administration questions in the forums. Any other messages are welcome.


Log in with your credentials


Forgot your details?

Create Account