简体   繁体   English

PS流水线多参数绑定

[英]PS Pipeline Multiple Parameters Binding

I am trying to pass two parameters to a function through the pipeline but it does not appear to work as expected and I am struggling to understand why.我试图通过管道将两个参数传递给一个函数,但它似乎没有按预期工作,我正在努力理解为什么。

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

Output输出

Name: Value名称:值

Value: Value价值:价值

I tried running the Trace-Command command to see what was happening.我尝试运行Trace-Command命令来查看发生了什么。 On line 35 we can see that Value is bound to $Parameter .在第 35 行,我们可以看到Value绑定到$Parameter

Why is PowerShell binding the second input to both parameters?为什么 PowerShell 将第二个输入绑定到两个参数? If this is expected, why does it occur only for the second parameter and not for the first?如果这是预期的,为什么它只发生在第二个参数而不是第一个参数?

Trace痕迹

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

Per the comment from Lee Dailey, you can only have one parameter accept input from the Pipeline by "Value" (for each value type, eg string, int etc.). 根据Lee Dailey的评论,您只能通过“值”从管道接受一个参数接受输入(对于每个值类型,例如字符串,整数等)。 What your code is currently doing is sending an array of string values, which would then be processed one at a time through the pipeline. 您的代码当前正在做的是发送一个字符串值数组,然后通过管道一次处理一个。

If you want to send in multiple values together in to the Pipeline, you could do so by making those values the properties of a custom object, and then you can accept them via the pipeline by using ValueFromPipelineByPropertyName parameters. 如果要将多个值一起发送到管道,可以通过将这些值作为自定义对象的属性来实现,然后可以使用ValueFromPipelineByPropertyName参数通过管道接受它们。 This works by matching any properties of the input object that have the same name as input parameters: 这通过匹配与输入参数具有相同名称的输入对象的任何属性来工作:

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

Result: 结果:

Name: MyName
Value: MyValue

An alternative but similar approach is to use ValueFromPipeline to accept an input object, and then get the property values from that object: 另一ValueFromPipeline似的方法是使用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

Some cmdlets will support both of these approaches, as PowerShell will try to match by object type first and then will revert to matching by property name. 某些cmdlet将支持这两种方法,因为PowerShell将首先尝试按对象类型进行匹配,然后将按属性名称还原为匹配。 There's a detailed explanation of this here if you'd like to know more: https://blogs.technet.microsoft.com/heyscriptingguy/2013/03/25/learn-about-using-powershell-value-binding-by-property-name/ 如果您想了解更多信息, 在此处详细解释: https//blogs.technet.microsoft.com/heyscriptingguy/2013/03/25/learn-about-using-powershell-value-binding-by-属性名称/

Note if you are going to work with values successfully via the Pipeline you also need to use a Process { } block in your function, which results in collections of objects being processed one at a time: 请注意,如果要通过管道成功处理值,则还需要在函数中使用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

Without this only the last value in the object collection would be handled. 如果没有这个,只会处理对象集合中的最后一个值。

In addition to @Mark's answer, as mentioned ValueFromPipeline attribute can be set only once per parameter type.除了@Mark 的回答,如前所述,每个参数类型只能设置一次 ValueFromPipeline 属性。
This is the example where multiple ValueFromPipeline are used, each for a different parameter type.这是使用多个 ValueFromPipeline 的示例,每个用于不同的参数类型。

Only one parameter is assigned per iteration.每次迭代只分配一个参数。 The one matching the input value's type.匹配输入值类型的那个。

Note:笔记:
$Int contains 0 when no specific value is used on initialization.当初始化时没有使用特定值时,$Int 包含0
It's safer to check which parameter set was used and not only if value was assigned.检查使用了哪个参数集更安全,而不仅仅是是否分配了值。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM