Home > Mobile >  How are parameters bound to second position in pipeline?
How are parameters bound to second position in pipeline?

Time:12-05

I'm trying to understand how parameters are bound to the second position of a cmdlet, within a pipeline.

Tried with Select-String [1]. It has -Path, which accepts pipeline input at position 1 (second position). These work:

sls "the-pattern" "the-file.txt"
"the-file.txt" | sls "the-pattern" -path {$_}

But this doesn't:

"the-file.txt" | sls "the-pattern"

In this latter case, I expect "the-file.txt" to be bound as the 2nd argument to sls.

How do parameter binding work after the first position, in Powershell?

[1] https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/select-string?view=powershell-7.3#parameters

CodePudding user response:

Get-Help provides crucial information to answer your question:

Get-Help Select-String -Parameter LiteralPath, Path, Pattern, InputObject |
    Select-Object Name, Position, PipelineInput, Aliases

We can see the following:

name        position pipelineInput         aliases
----        -------- -------------         -------
LiteralPath named    True (ByPropertyName) PSPath, LP
Path        1        True (ByPropertyName) none
InputObject named    True (ByValue)        none
Pattern     0        False                 none

Both -LiteralPath and -Path are bound ByPropertyName whereas -InputObject is bound ByValue.

Knowing this we can assume the following, in the first example:

sls "the-pattern" "the-file.txt"
  • "the-pattern" is bound positionally to -Pattern.
  • "the-file.txt" is bound positionally to -Path.

In the second example:

"the-file.txt" | sls "the-pattern" -path {$_}

In third example, which works perfectly fine except it doesn't work how you expected it to work:

"the-file.txt" | sls "the-pattern"
  • "the-file.txt" is bound to -InputObject by ValueFromPipeline.
  • "the-pattern" is bound positionally to -Pattern.

You can see this is true by simply trying "the-file.txt" | sls "the-file".

As for things you didn't try, Trace-Command helps a lot understanding how parameters are bound. This particular case is hard to understand at first because Select-String will always bind -InputObject from pipeline, no matter what (this happens because the parameter type is PSObject) and most likely has internal logic determining when it should treat the object coming from pipeline as a file and read from it's path.

Here is an example of what I mean, using a temporary file. $tmp should be bound to -LiteralPath because the object has the .PSPath property but instead it gets bound to -InputObject:

$tmp = New-TemporaryFile
$tmp = Get-Item $tmp.FullName
Trace-Command ParameterBinding { $tmp | sls .   } -PSHost

Output

BIND NAMED cmd line args [Select-String]
BIND POSITIONAL cmd line args [Select-String]   
    BIND arg [. ] to parameter [Pattern]        
        Binding collection parameter Pattern: 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 

        BIND arg [System.String[]] to param [Pattern] SUCCESSFUL
MANDATORY PARAMETER CHECK on cmdlet [Select-String]
CALLING BeginProcessing
BIND PIPELINE object to parameters: [Select-String]
    PIPELINE object TYPE = [System.IO.FileInfo] 
    RESTORING pipeline parameter's original values
    Parameter [InputObject] PIPELINE INPUT ValueFromPipeline NO COERCION
    BIND arg [...\AppData\Local\Temp\tmpD30C.tmp] to parameter [InputObject]
        BIND arg [...\AppData\Local\Temp\tmpD30C.tmp] to param [InputObject] SUCCESSFUL
MANDATORY PARAMETER CHECK on cmdlet [Select-String]
CALLING ProcessRecord
CALLING EndProcessing
  • Related