Связывание нескольких параметров конвейера PS
Я пытаюсь передать два параметра в функцию через конвейер, но он не работает должным образом, и я пытаюсь понять, почему.
MWE
function Test-Pipeline {
[CmdletBinding ()]
Param(
[Parameter(ValueFromPipeline=$true)][String]$Name,
[Parameter(ValueFromPipeline=$true)][String]$Value
)
Write-Host "Name: $Name"
Write-Host "Value: $Value"
}
"Name", "Value" | Test-Pipeline
Выход
Имя: Значение
Значение: Значение
Я пытался запустить Trace-Command
Команда, чтобы увидеть, что происходит. В строке 35 мы видим, что Value
связан с $Parameter
,
Почему PowerShell связывает второй вход с обоими параметрами? Если это ожидается, почему это происходит только для второго параметра, а не для первого?
след
DEBUG: ParameterBinding Information: 0 : BIND NAMED cmd line args [Test-Pipeline]
DEBUG: ParameterBinding Information: 0 : BIND POSITIONAL cmd line args [Test-Pipeline]
DEBUG: ParameterBinding Information: 0 : MANDATORY PARAMETER CHECK on cmdlet [Test-Pipeline]
DEBUG: ParameterBinding Information: 0 : BIND arg [] to parameter [Name]
DEBUG: ParameterBinding Information: 0 : Executing DATA GENERATION metadata: [System.Management.Automation.ArgumentTypeConverterAttribute]
DEBUG: ParameterBinding Information: 0 : result returned from DATA GENERATION:
DEBUG: ParameterBinding Information: 0 : COERCE arg to [System.String]
DEBUG: ParameterBinding Information: 0 : Parameter and arg types the same, no coercion is needed.
DEBUG: ParameterBinding Information: 0 : BIND arg [] to param [Name] SUCCESSFUL
DEBUG: ParameterBinding Information: 0 : BIND arg [] to parameter [Value]
DEBUG: ParameterBinding Information: 0 : Executing DATA GENERATION metadata: [System.Management.Automation.ArgumentTypeConverterAttribute]
DEBUG: ParameterBinding Information: 0 : result returned from DATA GENERATION:
DEBUG: ParameterBinding Information: 0 : COERCE arg to [System.String]
DEBUG: ParameterBinding Information: 0 : Parameter and arg types the same, no coercion is needed.
DEBUG: ParameterBinding Information: 0 : BIND arg [] to param [Value] SUCCESSFUL
DEBUG: ParameterBinding Information: 0 : CALLING BeginProcessing
DEBUG: ParameterBinding Information: 0 : BIND PIPELINE object to parameters: [Test-Pipeline]
DEBUG: ParameterBinding Information: 0 : PIPELINE object TYPE = [System.String]
DEBUG: ParameterBinding Information: 0 : RESTORING pipeline parameter's original values
DEBUG: ParameterBinding Information: 0 : Parameter [Value] PIPELINE INPUT ValueFromPipeline NO COERCION
DEBUG: ParameterBinding Information: 0 : BIND arg [Name] to parameter [Value]
DEBUG: ParameterBinding Information: 0 : Executing DATA GENERATION metadata: [System.Management.Automation.ArgumentTypeConverterAttribute]
DEBUG: ParameterBinding Information: 0 : result returned from DATA GENERATION: Name
DEBUG: ParameterBinding Information: 0 : BIND arg [Name] to param [Value] SUCCESSFUL
DEBUG: ParameterBinding Information: 0 : Parameter [Name] PIPELINE INPUT ValueFromPipeline NO COERCION
DEBUG: ParameterBinding Information: 0 : BIND arg [Name] to parameter [Name]
DEBUG: ParameterBinding Information: 0 : Executing DATA GENERATION metadata: [System.Management.Automation.ArgumentTypeConverterAttribute]
DEBUG: ParameterBinding Information: 0 : result returned from DATA GENERATION: Name
DEBUG: ParameterBinding Information: 0 : BIND arg [Name] to param [Name] SUCCESSFUL
DEBUG: ParameterBinding Information: 0 : MANDATORY PARAMETER CHECK on cmdlet [Test-Pipeline]
DEBUG: ParameterBinding Information: 0 : BIND PIPELINE object to parameters: [Test-Pipeline]
DEBUG: ParameterBinding Information: 0 : PIPELINE object TYPE = [System.String]
DEBUG: ParameterBinding Information: 0 : RESTORING pipeline parameter's original values
DEBUG: ParameterBinding Information: 0 : Parameter [Name] PIPELINE INPUT ValueFromPipeline NO COERCION
DEBUG: ParameterBinding Information: 0 : BIND arg [Value] to parameter [Name]
DEBUG: ParameterBinding Information: 0 : Executing DATA GENERATION metadata: [System.Management.Automation.ArgumentTypeConverterAttribute]
DEBUG: ParameterBinding Information: 0 : result returned from DATA GENERATION: Value
DEBUG: ParameterBinding Information: 0 : BIND arg [Value] to param [Name] SUCCESSFUL
DEBUG: ParameterBinding Information: 0 : Parameter [Value] PIPELINE INPUT ValueFromPipeline NO COERCION
DEBUG: ParameterBinding Information: 0 : BIND arg [Value] to parameter [Value]
DEBUG: ParameterBinding Information: 0 : Executing DATA GENERATION metadata: [System.Management.Automation.ArgumentTypeConverterAttribute]
DEBUG: ParameterBinding Information: 0 : result returned from DATA GENERATION: Value
DEBUG: ParameterBinding Information: 0 : BIND arg [Value] to param [Value] SUCCESSFUL
DEBUG: ParameterBinding Information: 0 : MANDATORY PARAMETER CHECK on cmdlet [Test-Pipeline]
DEBUG: ParameterBinding Information: 0 : CALLING EndProcessing
DEBUG: ParameterBinding Information: 0 : BIND NAMED cmd line args [Write-Host]
DEBUG: ParameterBinding Information: 0 : BIND POSITIONAL cmd line args [Write-Host]
DEBUG: ParameterBinding Information: 0 : BIND REMAININGARGUMENTS cmd line args to param: [Object]
DEBUG: ParameterBinding Information: 0 : BIND arg [System.Collections.Generic.List`1[System.Object]] to parameter [Object]
DEBUG: ParameterBinding Information: 0 : COERCE arg to [System.Object]
DEBUG: ParameterBinding Information: 0 : Parameter and arg types the same, no coercion is needed.
DEBUG: ParameterBinding Information: 0 : BIND arg [System.Collections.Generic.List`1[System.Object]] to param [Object] SUCCESSFUL
DEBUG: ParameterBinding Information: 0 : MANDATORY PARAMETER CHECK on cmdlet [Write-Host]
DEBUG: ParameterBinding Information: 0 : CALLING BeginProcessing
Name: Value
DEBUG: ParameterBinding Information: 0 : CALLING EndProcessing
DEBUG: ParameterBinding Information: 0 : BIND NAMED cmd line args [Write-Host]
DEBUG: ParameterBinding Information: 0 : BIND POSITIONAL cmd line args [Write-Host]
DEBUG: ParameterBinding Information: 0 : BIND REMAININGARGUMENTS cmd line args to param: [Object]
DEBUG: ParameterBinding Information: 0 : BIND arg [System.Collections.Generic.List`1[System.Object]] to parameter [Object]
DEBUG: ParameterBinding Information: 0 : COERCE arg to [System.Object]
DEBUG: ParameterBinding Information: 0 : Parameter and arg types the same, no coercion is needed.
DEBUG: ParameterBinding Information: 0 : BIND arg [System.Collections.Generic.List`1[System.Object]] to param [Object] SUCCESSFUL
DEBUG: ParameterBinding Information: 0 : MANDATORY PARAMETER CHECK on cmdlet [Write-Host]
DEBUG: ParameterBinding Information: 0 : CALLING BeginProcessing
Value: Value
DEBUG: ParameterBinding Information: 0 : CALLING EndProcessing
2 ответа
Согласно комментарию Ли Дейли, у вас может быть только один параметр, принимающий входные данные из конвейера через "Значение" (для каждого типа значения, например, string, int и т. Д.). В настоящее время ваш код отправляет массив строковых значений, который затем обрабатывается по одному через конвейер.
Если вы хотите отправить несколько значений вместе в конвейер, вы можете сделать это, сделав эти значения свойствами пользовательского объекта, а затем вы можете принять их через конвейер, используя ValueFromPipelineByPropertyName
параметры. Это работает путем сопоставления любых свойств входного объекта, имя которого совпадает с именем входных параметров:
function Test-Pipeline {
[CmdletBinding ()]
Param(
[Parameter(ValueFromPipelineByPropertyName=$true)][String]$Name,
[Parameter(ValueFromPipelineByPropertyName=$true)][String]$Value
)
Write-Host "Name: $Name"
Write-Host "Value: $Value"
}
$MyObject = [pscustomobject]@{
Name = "MyName"
Value = "MyValue"
}
$MyObject | Test-Pipeline
Результат:
Name: MyName
Value: MyValue
Альтернативный, но похожий подход заключается в использовании ValueFromPipeline
принять входной объект, а затем получить значения свойств из этого объекта:
function Test-Pipeline {
[CmdletBinding ()]
Param(
[Parameter(ValueFromPipeline=$true)][Object]$InputObject
)
$Name = $InputObject.Name
$Value = $InputObject.Value
Write-Host "Name: $Name"
Write-Host "Value: $Value"
}
$MyObject = [pscustomobject]@{
Name = "MyName"
Value = "MyValue"
}
$MyObject | Test-Pipeline
Некоторые командлеты будут поддерживать оба этих подхода, поскольку PowerShell сначала попытается сопоставить по типу объекта, а затем вернется к сопоставлению по имени свойства. Здесь вы найдете подробное объяснение этого, если вы хотите узнать больше: https://blogs.technet.microsoft.com/heyscriptingguy/2013/03/25/learn-about-using-powershell-value-binding-by-property-name/
Обратите внимание, что если вы собираетесь успешно работать со значениями через конвейер, вам также необходимо использовать Process { }
блокировать в вашей функции, что приводит к тому, что коллекции объектов обрабатываются по одному:
function Test-Pipeline {
[CmdletBinding ()]
Param(
[Parameter(ValueFromPipeline=$true)][Object]$InputObject
)
Process {
$Name = $InputObject.Name
$Value = $InputObject.Value
Write-Host "Name: $Name"
Write-Host "Value: $Value"
}
}
$MyObject = @(
[pscustomobject]@{
Name = "MyName"
Value = "MyValue"
}
[pscustomobject]@{
Name = "MySecondName"
Value = "MySecondValue"
}
)
$MyObject | Test-Pipeline
Без этого будет обрабатываться только последнее значение в коллекции объектов.
В дополнение к ответу @Mark, как уже упоминалось, атрибут ValueFromPipeline может быть установлен только один раз для каждого типа параметра.
Это пример, где используется несколько ValueFromPipeline, каждый для другого типа параметра.
На итерацию назначается только один параметр. Тот, который соответствует типу входного значения.
Примечание:
$Int содержит
0
когда при инициализации не используется конкретное значение.
Безопаснее проверять, какой набор параметров использовался, а не только, было ли присвоено значение.
function testus() {
param(
[Parameter(ValueFromPipeline, ParameterSetName = "A")]
[String]$Str,
[Parameter(ValueFromPipeline, ParameterSetName = "B")]
[Int]$Int
)
process {
if ($PSCmdlet.ParameterSetName -eq 'A') {
Write-Host ""
Write-Host "String was used:"
Write-Host "Str: $Str"
Write-Host "(Int contains initialized default value '0': $Int)"
} elseif ($PSCmdlet.ParameterSetName -eq 'B') {
Write-Host ""
Write-Host "Integer was used:"
Write-Host "Int: $Int"
Write-Host "(Str contains initialized default value '' (empty string): $($Str.gettype().FullName))"
}
}
}
'a', 1, 0 | testus