Author Posts

April 11, 2014 at 5:30 am

Basically, the scenario is user is providing a CSV. We'll say there are two params First and Second. If the user does not provide a Second param in the CSV, the goal is to use the default function param. So, the function:

Function Test-It{
Param(
[Parameter(Mandatory=$false,
ValueFromPipeline=$True,
ValueFromPipelineByPropertyName=$True,
HelpMessage='The first parameter')]
[AllowNull()]
[AllowEmptyString()]
[string]$First ="FirstDefault",
[Parameter(Mandatory=$True,
ValueFromPipeline=$True,
ValueFromPipelineByPropertyName=$True,
HelpMessage='The second parameter')]
[AllowNull()]
[AllowEmptyString()]
[string]$Second ="SecondDefault" )

begin{}
process{
Write-Host ("First: {0}" -f $First)
Write-Host ("Second: {0}" -f $Second)
}
end{}
}

If I do Test-It -First "Whatever", the default works correctly. If I pass both params over the pipeline:

$parameters = New-Object -TypeName PSObject -Property @{
First = "Hello"
Second = "Rob"
}

Clear-Host
$parameters | Test-It

Returns:
First: Hello
Second: Rob

But if I only pass one parameter over the pipeline:

$parameters = New-Object -TypeName PSObject -Property @{
First = "Hello"
}

Clear-Host
$parameters | Test-It

Then it returns like this when I expect Second: SecondDefault:

First: Hello
Second: @{First=Hello}

How do input default params are utilizing when using the pipeline?

April 11, 2014 at 5:36 am

Try getting rid of the "ValueFromPipeline" attributes, and just stick with ValueFromPipelineByPropertyName.

April 11, 2014 at 5:42 am

Ok, that did work. When you use the ValueFromPipeLine, is that insinuating that everything the function will need is coming from the pipleline. I'm trying to understand this behavior so I know when and when not to use that parameter option. Can you elaborate? Thank you for the quick response.

April 11, 2014 at 5:52 am

Pipeline input works in one of 4 ways (attempted in this order, if I remember correctly): By Value (no coercion), By Property Name (no coercion), By Value (with coercion), By Property Name (with coercion). You've defined your two input parameters to be of type [String], but piped in a PSObject. By Value (no coercion) doesn't work, because the type of the input object doesn't match either parameter. By Propety Name (no coercion) works, for whichever properties exist as Strings on your input object. By Value (with coercion) will ALWAYS succeed for any remaining unbound String parameters, because every object can be represented as a string via the Object.ToString() method. That's where your function was running into a problem.

Having ValueFromPipeline applied to multiple parameters in the same parameter set is usually pointless. Typically, you'll see one parameter with ValueFromPipeline (and possibly ValueFromPipelineByPropertyName as well), and any number of other parameters with ValueFromPipelineByPropertyNaem only.