【问题标题】:Powershell Array parameter from pipline来自管道的 Powershell 数组参数
【发布时间】:2014-08-30 21:54:36
【问题描述】:

我正在尝试使用CmdletBindingValueFromPipeline 复制管道中可用的典型powershell -Computername 参数并作为普通参数。我的挑战是,我从指定参数与值中的管道得到不同的结果。

我的代码如下所示:

[CmdletBinding()]
param(
    [parameter(Mandatory=$true, ValueFromPipeline=$true)] [string[]]$ComputerName
 )

BEGIN {  "Begin script`n-----" }

PROCESS { 
    "  `$ComputerName '$ComputerName'"
    "  `$_ '$_'"
    "  +++ Count: " + $ComputerName.Count
    foreach($computer in $ComputerName) {
        "    `$computer '$computer'"
    }
    "  -----"
}

END { "Complete" }

当我使用管道运行它时,我得到了这个:

PS> (1, 2, 3) | .\BPEParamTest.ps1 开始脚本 ----- $计算机名'1' $_ '1' +++ 计数:1 $计算机'1' ----- $计算机名'2' $_'2' +++ 计数:1 $计算机'2' ----- $计算机名'3' $_'3' +++ 计数:1 $电脑'3' ----- 完全的

但是,当使用参数运行时,我会得到不同的结果:

PS> .\BPEParamTest.ps1 -计算机名 (1, 2, 3) 开始脚本 ----- $计算机名'1 2 3' $_'' +++ 计数:3 $计算机'1' $计算机'2' $电脑'3' ----- 完全的

【问题讨论】:

  • 我希望有人能给出一个好的答案。几天来,我也一直在努力解决这个问题。我注意到,如果我没有 Begin/Process/End 块并且只是将代码包含在基本函数中,它的行为也会有所不同,所以我认为它与块有关。
  • 所以我的最终结果是终止管道使用,并使用基于参数的计算机名数组。这最终更符合几个内置命令的工作方式,例如 get-service(管道是服务名称,而不是计算机名称)、get-process(管道是进程名称)和测试连接。这些都使用 Computername 和 ByPropertyName 的东西。最终结果是使用 -ComputerName (Get-Content Servers.txt) 而不是 Get-Content Servers.txt | 运行它

标签: powershell


【解决方案1】:

我总是使用以下结构。这适用于参数中的数组以及管道中的数组:

[CmdletBinding()]
param(
    [parameter(Mandatory=$true, ValueFromPipeline=$true)] [string[]]$ComputerName
 )

process {
  foreach($computer in $computername){
      #do the stuff
  }

完整解释:流程块为管道中的每个项目运行一次,这就是它处理管道上列表的方式(即 $computername 依次设置为每个项目)。如果您将值作为参数传递,则 $computername 将设置为列表,这就是存在循环的原因。

【讨论】:

  • 如果馆藏很大或收集起来很昂贵,这种方法的效果会降低。
  • 我不确定你的意思。如果您通过管道输入事物列表,则流程块会为每个项目运行一次。它们根本不被收集。该循环用于在命令行上作为参数显示的数组。
  • 如果集合很昂贵并且作为参数传递,PowerShell 会在调用函数之前收集整个集合。 (我在考虑foreach 而不是管道。)
  • 完全同意在大集合中使用 foreach 关键字。
  • 所以这最终会像我上面的示例一样(错误地)工作 - 当服务器列表通过管道传输到脚本时,管道中的每个项目都会调用一次 PROCESS,当使用参数调用时,PROCESS 是调用一次。
【解决方案2】:

.\BPEParamTest.ps1 -ComputerName (1, 2, 3) 不使用管道,而是使用单个输入(一个 3 元素数组)。相比之下,(1, 2, 3) | .\BPEParamTest.ps1 使用管道(3 个单独的输入)。

要解决此问题,您可以检查绑定参数并确定是否有管道输入。这是一个简短的例子:

function out-item {
  [CmdletBinding()]
  param(
    [Parameter(ValueFromPipeline=$TRUE)]
      $item
  )
  begin {
    $PipelineInput = -not $PSBoundParameters.ContainsKey("item")
    write-host "Pipeline input? $PipelineInput"
    $n = 0
  }
  process {
    if ($PipelineInput) {
      $_
      $n++
    }
    else {
      $item | foreach-object {
        $_
        $n++
      }
    }
  }
  end {
    write-host "Output $n item(s)"
  }
}

使用此函数,1,2,3 | out-itemout-item 1,2,3 都产生相同的输出(“管道输入?”部分除外)。

【讨论】:

  • 主要问题是让 Process 为参数运行一次而为管道运行多次的愚蠢。您还必须在函数中进行主要处理:if ($PipelineInput) { DoStuff -computer $_ } else { $item | foreach-object { DoStuff -computer $item } } 理想的方法是在开始时将参数转换为管道。如果有人同时使用管道和参数调用它,所有这些都将成为废话:-)
  • 进程块是没有问题的(事实上,如果不声明它在所有函数中都是隐含的)。此外,一个参数不能在同一调用中同时输入管道 参数;这是非此即彼的定义。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-04-11
  • 2015-02-18
  • 1970-01-01
  • 1970-01-01
  • 2017-04-17
  • 2014-01-31
  • 2018-04-06
相关资源
最近更新 更多