3

In a PowerShell script, I want to read a CSV file that contains something like this:

Type  Title      Param1        Param2       
----  -----      ------        ------       
Type1 Foo type 1 ValueForType1              
Type2 Foo type 2               ValueForType2

When type is Type1, I have to call a function named New-FooType1, when type is Type2, the funcation is named New-FooType2, and so on:

function New-FooType1{
    param(
        [Parameter(Mandatory=$true, ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
        [string]$Title,
        [Parameter(Mandatory=$true, ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
        [string]$Param1
    )

    Write-Host "New-FooType1 $Title with $Param1"
}

function New-FooType2{
    param(
        [Parameter(Mandatory=$true, ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
        [string]$Title,
        [Parameter(Mandatory=$true, ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
        [string]$Param2
    )

    Write-Host "New-FooType2 $Title with $Param2"
}

I'm trying to route the call to either of the functions, using a dynamic invocation:

$csv | % {
    $cmdName = "New-Foo$($_.Type)"
    Invoke-Command (gcm $cmdName) -InputObject $_

}

However, I always get an error:

Parameter set cannot be resolved using the specified named parameters

As you can see, different type mean different parameters set.

How can I solve this? I would like to avoid manipulating properties manually, because in my real life script, I have a dozen of different types, with up to 6 parameters.

Here is a complete repro sample of the issue:

$csvData = "Type;Title;Param1;Param2`nType1;Foo type 1;ValueForType1;;`nType2;Foo type 2;;ValueForType2"

$csv = ConvertFrom-csv $csvData -Delimiter ';'

$csv | ft -AutoSize

function New-FooType1{
    param(
        [Parameter(Mandatory=$true, ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
        [string]$Title,
        [Parameter(Mandatory=$true, ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
        [string]$Param1
    )

    Write-Host "New-FooType1 $Title with $Param1"
}

function New-FooType2{
    param(
        [Parameter(Mandatory=$true, ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
        [string]$Title,
        [Parameter(Mandatory=$true, ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
        [string]$Param2
    )

    Write-Host "New-FooType2 $Title with $Param2"
}


$csv | % {
    $cmdName = "New-Foo$($_.Type)"
    Invoke-Command (gcm $cmdName) -InputObject $_

}

The expected output of this script is:

New-FooType1 Foo type 1 with ValueForType1
New-FooType2 Foo type 2 with ValueForType2
4
  • $_|&"New-Foo$($_.Type)" Commented Dec 9, 2015 at 10:42
  • @PetSerAl: this works! you should post an answer. I'll reward you Commented Dec 9, 2015 at 10:48
  • In most cases I would just modify the function to accept a Type by parameter value. For example New-Foo -Type 'Type2' or New-Foo -Type 'Type1'. Then it's easy to just pass in the correct parameter. Commented Dec 9, 2015 at 13:03
  • @DarkLite1: most of time I'd agree. But in this specific case, I have too much Type values, and I think having separate methods will be easier to handle than multlipliying parametersetname Commented Dec 9, 2015 at 14:06

3 Answers 3

4

Use the call operator &:

$CmdName = "New-FooType1"
$Arguments = "type1"

& $CmdName $Arguments

the call operator also supports splatting if you want the arguments bound to specific named parameters:

$Arguments = @{
    "title" = "type1"
}

& $CmdName @Arguments
Sign up to request clarification or add additional context in comments.

Comments

2

To invoke command by name you should use invoke operator &. Invoke-Command cmdlet support only ScriptBlock and file invocation, and file invocation only supported for remote calls.

For dynamic parameter binding you can use spatting, but in that case you have to convert PSCustomObjects, returned by ConvertFrom-Csv cmdlet, to Hashtable. You also have to strip any extra parameters from Hashtable because splatting will fail if you try to bind non-existing parameter.

Another approach for dynamic parameter binding would be to use binding from pipeline object. It looks like it is what you want to do, since you mark all your parameters with ValueFromPipelineByPropertyName option. And this approach will just ignore any extra property it can not bind to parameter. I recommend you to remove ValueFromPipeline option, because with this option in case of absence of property with parameter name PowerShell will just convert PSCustomObject to string (or to whatever type you use for parameter) and pass it as value for parameter.

So, all you need is to pass object by pipeline and use invoke operator for invocation of command with dynamic name:

$_ | & "New-Foo$($_.Type)"

1 Comment

Not sure to understand the difference with my first attempt, but it works. thanks
-2

dont know exactly what your trying to do, but Invoke-Command (gcm $cmdName) ?

Try invoke-expression $cmdname

1 Comment

how do you pass the parameters with invoke-expression

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.