Simon Elms
Simon Elms

Reputation: 19728

Passing function arguments by position: Do explicit values get used before pipeline input?

I'm experimenting with creating a function that can take multiple arguments by position, including pipeline input.

Here's a simple test function:

function TestMultiplePositionalParameters
{
    param
    (  
        [Parameter(
            Position=0, 
            Mandatory=$true)
        ]
        [String]$FirstParam,

        [Parameter(
            Position=1, 
            Mandatory=$true, 
            ValueFromPipeline=$true)
        ]
        [String]$SecondParam
    ) 

    begin
    {
        Write-Host '================================='
    }

    process
    {
        Write-Host '$FirstParam:' $FirstParam
        Write-Host '$SecondParam:' $SecondParam
        Write-Host ''        
    }
}

When I call it, it works fine:

"Input1","Input2" | TestMultiplePositionalParameters 'ExplicitArgument'

Which results in:

=================================
$FirstParam: ExplicitArgument
$SecondParam: Input1

$FirstParam: ExplicitArgument
$SecondParam: Input2

However, if I change the parameter which takes the value from the pipeline:

function TestMultiplePositionalParameters
{
    param
    (  
        [Parameter(
            Position=0, 
            Mandatory=$true, 
            ValueFromPipeline=$true)
        ]
        [String]$FirstParam,

        [Parameter(
            Position=1, 
            Mandatory=$true)
        ]
        [String]$SecondParam
    ) 

    # etc...

}

And again call it:

"Input1","Input2" | TestMultiplePositionalParameters 'ExplicitArgument'

This time I get an error:

TestMultiplePositionalParameters : The input object cannot be bound to any parameters 
for the command either because the command does not take pipeline input or the input 
and its properties do not match any of the parameters that take pipeline input.
At C:\...\DEMO_Function_Pipeline_MultiPositionalParams.ps1:77 char:21
+ "Input1","Input2" | TestMultiplePositionalParameters 'ExplicitArgument'
+                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (Input1:String) 
   [TestMultiplePositionalParameters ], ParameterBindingException
    + FullyQualifiedErrorId : InputObjectNotBound,TestMultiplePositionalParameters

My question is: Why does the second version of the function pass the explicit argument 'ExplicitArgument' to the first parameter? I would have thought that given only the first parameter can accept pipeline input the function should have passed the pipeline input to the first parameter and the explicit argument to the second parameter.

Upvotes: 1

Views: 1198

Answers (1)

Swoogan
Swoogan

Reputation: 5558

Why does the second version of the function pass the explicit argument 'ExplicitArgument' to the first parameter?

Because $FirstParam is marked as being at position '0' and the first parameter passed to the cmdlet is 'ExplicitArgument'. Essentially, the binding of 'ExplicitArgument' to $FirstParam has already happened by the time PowerShell is looking for a parameter that will accept pipeline input.

To get your expected behaviour, you would have to execute the following:

"Input1","Input2" | TestMultiplePositionalParameters -SecondParam 'ExplicitArgument'

The reason for this is that you can have more than one parameter that accepts its value from the pipeline. If you had the following, what would you expect to happen in your second example?

param
(  
    [Parameter(
        Position=0, 
        Mandatory=$true, 
        ValueFromPipeline=$true)
    ]
    [String]$FirstParam,

    [Parameter(
        Position=1, 
        Mandatory=$true,
        ValueFromPipeline=$true)
    ]
    [String]$SecondParam
) 

There has to be a precedence and it is that named and positional arguments have higher priority than the pipeline arguments.

Upvotes: 1

Related Questions