PowerShell 中的 ParameterSetName 检测是否匹配 ValueFromPipeline 输入对象类型?

ParameterSetName detection in PowerShell functions matching on ValueFromPipeline input object type?

我在我编写的自定义函数中发现了一些 st运行ge 行为,因此我编写了一些具有不同特征的快速测试函数来展示这些行为。当参数集足够相似以至于唯一的区别因素是通过管道接收的对象的类型时,就会出现问题。

首先,我做了一个简单的类型,它的作用只是与字符串不同。

Add-Type @"
public class TestType {
   public string Prop1;
}
"@

接下来,我创建了一个测试函数,运行 它带有字符串和 TestType 输入。

function Test-ParameterSets1
{
    [CmdletBinding()]
    param (
        [Parameter(Mandatory=$true, ValueFromPipeline=$true, ParameterSetName="Str")] [string] $StringInput,
        [Parameter(Mandatory=$true, ValueFromPipeline=$true, ParameterSetName="Test")] [TestType] $TestInput
    )
    begin {
        $result = New-Object Object | Select-Object –Property @{n='FunctionName';e={$PSCmdlet.MyInvocation.InvocationName}},@{n='ParameterSetName';e={$PSCmdlet.ParameterSetName}}
    }
    process {
        $result | Add-Member -MemberType NoteProperty -Name StringInput -Value $StringInput -PassThru | Add-Member -MemberType NoteProperty -Name TestInput -Value $TestInput
    }
    end {
        $result
    }
}
'string' | Test-ParameterSets1
New-Object TestType | Test-ParameterSets1


FunctionName        ParameterSetName   StringInput TestInput
------------        ----------------   ----------- ---------
Test-ParameterSets1 __AllParameterSets string               
Test-ParameterSets1 __AllParameterSets             TestType 

这就是问题的核心。 ParameterSetName 的计算结果为 __AllParameterSets,即使从值中可以看出,参数已按预期设置。我的函数有很多参数集,并根据参数集进行大量切换以控制逻辑流程。

接下来我尝试添加一个参数集唯一的参数,正如预期的那样,ParameterSetName 对于仅在其中指定的调用是正确的。

function Test-ParameterSets2
{
    [CmdletBinding()]
    param (
        [Parameter(Mandatory=$true, ValueFromPipeline=$true, ParameterSetName="Str")] [string] $StringInput,
        [Parameter(Mandatory=$true, ValueFromPipeline=$true, ParameterSetName="Test")] [TestType] $TestInput,
        [Parameter(ParameterSetName="Test")] [string] $TestName
    )
    begin {
        $result = New-Object Object | Select-Object –Property @{n='FunctionName';e={$PSCmdlet.MyInvocation.InvocationName}},@{n='ParameterSetName';e={$PSCmdlet.ParameterSetName}}
    }
    process {
        $result | Add-Member -MemberType NoteProperty -Name StringInput -Value $StringInput -PassThru | Add-Member -MemberType NoteProperty -Name TestInput -Value $TestInput
    }
    end {
        $result
    }
}
'string' | Test-ParameterSets2
New-Object TestType | Test-ParameterSets2 -TestName MyName
New-Object TestType | Test-ParameterSets2


FunctionName        ParameterSetName   StringInput TestInput
------------        ----------------   ----------- ---------
Test-ParameterSets2 __AllParameterSets string               
Test-ParameterSets2 Test                           TestType 
Test-ParameterSets2 __AllParameterSets             TestType 

接下来,我尝试添加一个对两个参数集都是必需的参数,而这次 ParameterSetName 评估为空字符串,这尤其令人困惑。

function Test-ParameterSets5
{
    [CmdletBinding()]
    param (
        [Parameter(Mandatory=$true, ValueFromPipeline=$true, ParameterSetName="Str")] [string] $StringInput,
        [Parameter(Mandatory=$true, ValueFromPipeline=$true, ParameterSetName="Test")] [TestType] $TestInput,
        [Parameter(Mandatory=$true, ParameterSetName="Str")] [Parameter(Mandatory=$true, ParameterSetName="Test")] [string] $Mandatory,
        [Parameter(ParameterSetName="Test")] [string] $TestName
    )
    begin {
        $result = New-Object Object | Select-Object –Property @{n='FunctionName';e={$PSCmdlet.MyInvocation.InvocationName}},@{n='ParameterSetName';e={$PSCmdlet.ParameterSetName}}
    }
    process {
        $result | Add-Member -MemberType NoteProperty -Name StringInput -Value $StringInput -PassThru | Add-Member -MemberType NoteProperty -Name TestInput -Value $TestInput
    }
    end {
        $result
    }
}
'string' | Test-ParameterSets5 -Mandatory mandatoryParam
New-Object TestType | Test-ParameterSets5 -Mandatory mandatoryParam -TestName MyName
New-Object TestType | Test-ParameterSets5 -Mandatory mandatoryParam 


FunctionName        ParameterSetName StringInput TestInput
------------        ---------------- ----------- ---------
Test-ParameterSets5                  string               
Test-ParameterSets5 Test                         TestType 
Test-ParameterSets5                              TestType 

PowerShell 似乎确实知道如何正确设置这些参数,但 ParameterSetName 的计算不正确。有什么方法可以使它正常工作吗?我想避免使用不必要的开关,例如 -String 和 -TestType,它们对于它们自己的参数集是唯一的,以便 PowerShell 可以完成它的工作。谢谢!

您的代码存在问题,您读取了 begin 块中的 ParameterSetName 属性。当命令接受管道输入时,输入对象可以影响选定的 ParameterSetName。如果您的命令有多个输入对象,那么每个对象都会导致不同的参数集被选中:

class a { }
class b { }
class c { }
function f {
    param(
        [Parameter(ParameterSetName='a', ValueFromPipeline)][a]$a,
        [Parameter(ParameterSetName='b', ValueFromPipeline)][b]$b,
        [Parameter(ParameterSetName='c', ValueFromPipeline)][c]$c
    )
    begin {
        "ParameterSetName in begin block: $($PSCmdlet.ParameterSetName)"
    }
    process {
        "ParameterSetName in process block: $($PSCmdlet.ParameterSetName)"
    }
}
[a]::new(), [b]::new(), [c]::new() | f

# Result:
# ParameterSetName in begin block: __AllParameterSets
# ParameterSetName in process block: a
# ParameterSetName in process block: b
# ParameterSetName in process block: c

因此,如果您想知道输入对象绑定到您的命令后选择了哪个参数集,那么您应该阅读process块中的ParameterSetName

为了根据@PerSetAI 的建议完成示例,这里是测试进程块中参数集的相同示例函数。

function Test-ParameterSets1
{
    [CmdletBinding()]
    param (
        [Parameter(Mandatory=$true, ValueFromPipeline=$true, ParameterSetName="Str")] [string] $StringInput,
        [Parameter(Mandatory=$true, ValueFromPipeline=$true, ParameterSetName="Test")] [TestType] $TestInput
    )
    begin {
        $result = New-Object Object | Select-Object –Property @{n='FunctionName';e={$PSCmdlet.MyInvocation.InvocationName}},@{n='BeginParameterSetName';e={$PSCmdlet.ParameterSetName}}
    }
    process {
        $result | Add-Member -MemberType NoteProperty -Name ProcessParameterSetName -Value $PSCmdlet.ParameterSetName -PassThru `
        | Add-Member -MemberType NoteProperty -Name StringInput -Value $StringInput -PassThru `
        | Add-Member -MemberType NoteProperty -Name TestInput -Value $TestInput
    }
    end {
        $result
    }
}
'string' | Test-ParameterSets1
New-Object TestType | Test-ParameterSets1


FunctionName            : Test-ParameterSets1
BeginParameterSetName   : __AllParameterSets
ProcessParameterSetName : Str
StringInput             : string
TestInput               : 

FunctionName            : Test-ParameterSets1
BeginParameterSetName   : __AllParameterSets
ProcessParameterSetName : Test
StringInput             : 
TestInput               : TestType