Обнаружение ParameterSetName в функциях PowerShell, соответствующих типу входного объекта ValueFromPipeline?
Я вижу странное поведение в написанной мной пользовательской функции, и поэтому я написал несколько быстрых тестовых функций с различными характеристиками, чтобы продемонстрировать это поведение. Проблема возникает, когда наборы параметров достаточно похожи, и единственным дифференцирующим фактором является тип объекта, полученный через конвейер.
Во-первых, я сделал простой тип, который служит только для отличия от строки.
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 мог выполнять свою работу. Спасибо!
2 ответа
Проблема с вашим кодом в том, что вы читаете ParameterSetName
недвижимость в begin
блок. Когда команда принимает входные данные конвейера, объект ввода может влиять на выбранный 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
Таким образом, если вы хотите знать, какой набор параметров был выбран после того, как входной объект был привязан к вашей команде, то вам следует прочитать ParameterSetName
в process
блок.
Чтобы завершить пример, основанный на совете @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