Visualize parameter binding
Parameter binding is the process that PowerShell uses to determine which parameter set is being used and to associate (bind) values to the parameters of a command. These values can come from the command line and the pipeline.
The parameter binding process starts by binding named and positional command-line arguments. After binding command-line arguments, PowerShell tries to bind any pipeline input. There are two ways that values are bound from the pipeline. Parameters that accept pipeline input have one or both of the following attributes:
- ValueFromPipeline - The value from the pipeline is bound to the parameter based on its type. The type of the argument must match the type of the parameter.
- ValueFromPipelineByPropertyName - The value from the pipeline is bound to the parameter based on its name. The object in the pipeline must have a property that matches the name of the parameter or one of its aliases. The type of the property must match or be convertible to the type of the parameter.
For more information about parameter binding, see about_Parameter_Binding.
Use Trace-Command
to visualize parameter binding
Troubleshooting parameter binding issues can be challenging. You can use the Trace-Command cmdlet to visualize the parameter binding process.
Consider the following scenario. You have a directory with two text files, file1.txt
and
[file2].txt
.
PS> Get-ChildItem
Directory: D:\temp\test\binding
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 5/17/2024 12:59 PM 0 [file2].txt
-a--- 5/17/2024 12:59 PM 0 file1.txt
You want to delete the files by passing the filenames, through the pipeline, to the Remove-Item
cmdlet.
PS> 'file1.txt', '[file2].txt' | Remove-Item
PS> Get-ChildItem
Directory: D:\temp\test\binding
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 5/17/2024 12:59 PM 0 [file2].txt
Notice that Remove-Item
only deleted file1.txt
and not [file2].txt
. The filename includes
square brackets, which is treated as a wildcard expression. Using Trace-Command
, you can see that
the filename is being bound to the Path parameter of Remove-Item
.
Trace-Command -PSHost -Name ParameterBinding -Expression {
'[file2].txt' | Remove-Item
}
The output from Trace-Command
can be verbose. Each line of output is prefixed with a timestamp
and trace provider information. For the output of this example, the prefix information has been
removed to make it easier to read.
BIND NAMED cmd line args [Remove-Item]
BIND POSITIONAL cmd line args [Remove-Item]
BIND cmd line args to DYNAMIC parameters.
DYNAMIC parameter object: [Microsoft.PowerShell.Commands.FileSystemProviderRemoveItemDynamicParameters]
MANDATORY PARAMETER CHECK on cmdlet [Remove-Item]
CALLING BeginProcessing
BIND PIPELINE object to parameters: [Remove-Item]
PIPELINE object TYPE = [System.String]
RESTORING pipeline parameter's original values
Parameter [Path] PIPELINE INPUT ValueFromPipeline NO COERCION
BIND arg [[file2].txt] to parameter [Path]
Binding collection parameter Path: argument type [String], parameter type [System.String[]],
collection type Array, element type [System.String], no coerceElementType
Creating array with element type [System.String] and 1 elements
Argument type String is not IList, treating this as scalar
Adding scalar element of type String to array position 0
BIND arg [System.String[]] to param [Path] SUCCESSFUL
Parameter [Credential] PIPELINE INPUT ValueFromPipelineByPropertyName NO COERCION
Parameter [Credential] PIPELINE INPUT ValueFromPipelineByPropertyName NO COERCION
Parameter [Credential] PIPELINE INPUT ValueFromPipelineByPropertyName WITH COERCION
Parameter [Credential] PIPELINE INPUT ValueFromPipelineByPropertyName WITH COERCION
MANDATORY PARAMETER CHECK on cmdlet [Remove-Item]
CALLING ProcessRecord
CALLING EndProcessing
Using Get-Help
, you can see that the Path parameter of Remove-Item
accepts string objects
from the pipeline ByValue
or ByPropertyName
. LiteralPath accepts string objects from the
pipeline ByPropertyName
.
PS> Get-Help Remove-Item -Parameter Path, LiteralPath
-Path <System.String[]>
Specifies a path of the items being removed. Wildcard characters are permitted.
Required? true
Position? 0
Default value None
Accept pipeline input? True (ByPropertyName, ByValue)
Accept wildcard characters? true
-LiteralPath <System.String[]>
Specifies a path to one or more locations. The value of LiteralPath is used exactly as it's
typed. No characters are interpreted as wildcards. If the path includes escape characters,
enclose it in single quotation marks. Single quotation marks tell PowerShell not to interpret
any characters as escape sequences.
Required? true
Position? named
Default value None
Accept pipeline input? True (ByPropertyName)
Accept wildcard characters? false
The output of Trace-Command
shows that parameter binding starts by binding command-line parameters
followed by the pipeline input. You can see that Remove-Item
receives a string object from the
pipeline. That string object is bound to the Path parameter.
BIND PIPELINE object to parameters: [Remove-Item]
PIPELINE object TYPE = [System.String]
RESTORING pipeline parameter's original values
Parameter [Path] PIPELINE INPUT ValueFromPipeline NO COERCION
BIND arg [[file2].txt] to parameter [Path]
...
BIND arg [System.String[]] to param [Path] SUCCESSFUL
Since the Path parameter accepts wildcard characters, the square brackets represent a wildcard expression. However, that expression doesn't match any files in the directory. You need to use the LiteralPath parameter to specify the exact path to the file.
Get-Command
shows that the LiteralPath parameter accepts input from the pipeline
ByPropertyName
or ByValue
. And, that it has two aliases, PSPath
and LP
.
PS> (Get-Command Remove-Item).Parameters.LiteralPath.Attributes |
>> Select-Object ValueFrom*, Alias* | Format-List
ValueFromPipeline : False
ValueFromPipelineByPropertyName : True
ValueFromRemainingArguments : False
AliasNames : {PSPath, LP}
In this next example, Get-Item
is used to retrieve a FileInfo object. That object has a
property named PSPath.
PS> Get-Item *.txt | Select-Object PSPath
PSPath
------
Microsoft.PowerShell.Core\FileSystem::D:\temp\test\binding\[file2].txt
The FileInfo object is then passed to Remove-Item
.
Trace-Command -PSHost -Name ParameterBinding -Expression {
Get-Item *.txt | Remove-Item
}
For the output of this example, the prefix information has been removed and separated to show parameter binding for both commands.
In this output, you can see that Get-Item
binds the positional parameter value *.txt
to the
Path parameter.
BIND NAMED cmd line args [Get-Item]
BIND POSITIONAL cmd line args [Get-Item]
BIND arg [*.txt] to parameter [Path]
Binding collection parameter Path: argument type [String], parameter type [System.String[]],
collection type Array, element type [System.String], no coerceElementType
Creating array with element type [System.String] and 1 elements
Argument type String is not IList, treating this as scalar
Adding scalar element of type String to array position 0
BIND arg [System.String[]] to param [Path] SUCCESSFUL
BIND cmd line args to DYNAMIC parameters.
DYNAMIC parameter object: [Microsoft.PowerShell.Commands.FileSystemProviderGetItemDynamicParameters]
MANDATORY PARAMETER CHECK on cmdlet [Get-Item]
In the trace output for parameter binding, you can see that Remove-Item
receives a FileInfo
object from the pipeline. Since a FileInfo object isn't a String object, it can't be bound
to the Path parameter.
The PSPath property of the FileInfo object matches an alias for the LiteralPath parameter. PSPath is also a String object, so it can be bound to the LiteralPath parameter without type coercion.
BIND NAMED cmd line args [Remove-Item]
BIND POSITIONAL cmd line args [Remove-Item]
BIND cmd line args to DYNAMIC parameters.
DYNAMIC parameter object: [Microsoft.PowerShell.Commands.FileSystemProviderRemoveItemDynamicParameters]
MANDATORY PARAMETER CHECK on cmdlet [Remove-Item]
CALLING BeginProcessing
CALLING BeginProcessing
CALLING ProcessRecord
BIND PIPELINE object to parameters: [Remove-Item]
PIPELINE object TYPE = [System.IO.FileInfo]
RESTORING pipeline parameter's original values
Parameter [Path] PIPELINE INPUT ValueFromPipeline NO COERCION
BIND arg [D:\temp\test\binding\[file2].txt] to parameter [Path]
Binding collection parameter Path: argument type [FileInfo], parameter type [System.String[]],
collection type Array, element type [System.String], no coerceElementType
Creating array with element type [System.String] and 1 elements
Argument type FileInfo is not IList, treating this as scalar
BIND arg [D:\temp\test\binding\[file2].txt] to param [Path] SKIPPED
Parameter [Credential] PIPELINE INPUT ValueFromPipelineByPropertyName NO COERCION
Parameter [Path] PIPELINE INPUT ValueFromPipelineByPropertyName NO COERCION
Parameter [Credential] PIPELINE INPUT ValueFromPipelineByPropertyName NO COERCION
Parameter [LiteralPath] PIPELINE INPUT ValueFromPipelineByPropertyName NO COERCION
BIND arg [Microsoft.PowerShell.Core\FileSystem::D:\temp\test\binding\[file2].txt] to parameter [LiteralPath]
Binding collection parameter LiteralPath: argument type [String], parameter type [System.String[]],
collection type Array, element type [System.String], no coerceElementType
Creating array with element type [System.String] and 1 elements
Argument type String is not IList, treating this as scalar
Adding scalar element of type String to array position 0
BIND arg [System.String[]] to param [LiteralPath] SUCCESSFUL
Parameter [Credential] PIPELINE INPUT ValueFromPipelineByPropertyName WITH COERCION
MANDATORY PARAMETER CHECK on cmdlet [Remove-Item]
CALLING ProcessRecord
CALLING EndProcessing
CALLING EndProcessing