【问题标题】:ValueFromPipeline behavior?ValueFromPipeline 行为?
【发布时间】:2012-02-17 13:07:03
【问题描述】:

我有一个命令Get-Testdata,它从不同的来源检索测试数据并将这些数据存储到PSObject 中,并将不同的值作为属性。然后将对象的总数存储为一个数组,以便于操作、排序、计算等。

我的问题是我希望能够将这些数据显示为(颜色编码的)HTML,为此我编写了另一个命令Show-TestResults。输入参数是这样的

[Parameter(Mandatory=$true,Position=0,ValueFromPipeline=$true)]
[PSObject[]]$InputObject

更新 1

这个函数本身非常基础,它只是为ConvertTo-HTML 设置一些参数,然后将对象通过管道传递到该命令中:

$head = "<style>[...]" #styling with javascript etc
$header = "<H1>Test Results</H1>
$title = "Test results"
$InputObject | ConvertTo-HTML -head $head -body $header -title $title | Out-File $Filename

结束更新 1

但是,当我尝试使用 ValueFromPipeline 属性时,使用调用

Get-Testdata [...] | Show-TestResults 

仅显示数组中的第一个对象。但是,如果我改为调用命令,例如

$td = Get-Testdata [...]
Show-TestResults $td 

按预期显示整个数组。有人可以解释一下吗?希望能指导我纠正它吗?

【问题讨论】:

  • 您想展示您的函数的外观(大致)以及您如何在那里使用 $InputObject 吗?如果那里有错误,我们会看到它。
  • 现已更新功能详情

标签: powershell


【解决方案1】:

您可能在结束块中处理数据,而不是处理块。

看一个例子:

function getdata {
    1
    2
    3
    4
}
function show-data {
    param(
        [Parameter(mandatory=$true, ValueFromPipeline=$true)]$InputObject,
        [Parameter(mandatory=$true)]$FileName
    )

    # this is process block that is probably missing in your code
    begin { $objects = @() }
    process { $objects += $InputObject }
    end {
        $head = "<style></style>"
        $header = "<H1>Test Results</H1>"
        $title = "Test results"
        $objects | ConvertTo-HTML -head $head -body $header -title $title | Out-File $Filename
    }
}

getdata | show-data -file d:\temp\test.html

【讨论】:

  • 谢谢,但正如您从我上面的更新中看到的那样,将 HTML 转换放入 process 块只会使最后一个帖子“通过”
  • 已更新。您需要收集对象并通过结束块。其他方法可能是使用代理函数,但这种方法更容易。
  • 谢谢,在process { } 期间将输入收集到另一个数组中起到了作用。但是我仍然很想知道为什么行为会根据参数接收数据的方式而有所不同,对我来说,它们的行为方式应该相同。
  • 有了更多的 Powershell 经验,我现在明白了区别:流水线将结果发送到 process {} 块,而“标准”参数将它们发送到 end {} 块(这意味着如果您不使用 begin{}process {}end {} 块中的任何一个,则整个功能)
【解决方案2】:

如果需要高级功能,那么我会按照@stej 提出的方式进行。

否则,当函数同时接受管道和参数输入时,我会考虑这种简单的技术:

function Show-Data
(
    $FileName,
    $InputObject
)
{
    # this is the trick:
    if ($InputObject) { $input = $InputObject }

    # process the input (from pipeline or parameter)
    $input | ConvertTo-HTML > $FileName
}

# pipe data
Get-ChildItem | Show-Data Test1.htm

# pass via parameter
Show-Data Test2.htm (Get-ChildItem)

注意$input 在这种情况下是管道输入的自动变量。

【讨论】:

  • 这也可以(我之前尝试过$input),但由于我想控制参数,@stej 上面的答案更适合我。
【解决方案3】:

我认为问题在于管道将您的数组展开为对象流,并一次将它们呈现给您的函数,而不是作为数组。

如果你这样做,它是否有效:

,(Get-Testdata [...]) | Show-TestResults 

【讨论】:

    【解决方案4】:

    我遇到了同样的问题/问题,我通常解决这个问题的方式是:

    Function Show-Data {
        param(
            [Parameter(mandatory=$true, ValueFromPipeline=$true)]$InputObject,
            [Parameter(mandatory=$true)]$FileName
        )
        $PipeLine = $Input | ForEach {$_}; If ($PipeLine) {$InputObject = $PipeLine}
        ...
    

    因为我认为覆盖automatic $Input variable 不是一个好主意。

    无论如何,我还没有看到问题部分的答案:“有人可以解释一下吗?
    我想这与Strongly Encouraged Development Guidelines 声明有关:

    支持 ProcessRecord 方法
    要在管道中接受来自上述 cmdlet 的所有记录, 您的 cmdlet 必须实现 ProcessRecord 方法。视窗 PowerShell 多次调用此方法,每条记录一次 发送到您的 cmdlet。

    ProcessRecord 方法在我看来是一个 C# 方法,我认为它是由process 块调用的,就像在stej 的解决方案中一样。但这并不能解释为什么这适用于 PSCustomObject 数组而不是例如系统对象,例如:

    Get-psdrive | Show-Data
    

    甚至:

    @(Get-psdrive) | Show-Data
    

    【讨论】:

      猜你喜欢
      • 2022-01-28
      • 1970-01-01
      • 2014-10-22
      • 1970-01-01
      • 2018-06-15
      • 1970-01-01
      • 2014-01-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多