问题 ValueFromPipeline的行为?


我有一个命令 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

END UPDATE 1

但是,当我尝试使用时 ValueFromPipeline 财产,使用电话

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

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

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

正如预期的那样呈现整个阵列。有人可以解释一下 - 并希望引导我纠正它吗?


12616
2018-02-17 13:07


起源

你想展示你的函数是什么样的(大约)以及你如何在那里使用$ InputObject?如果有错误,我们会看到它。 - Roman Kuzmin
现在更新功能详细信息 - Torbjörn Bergstedt


答案:


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

看一个例子:

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

9
2018-02-17 13:32



谢谢,但正如您从上面的更新中看到的那样,将HTML转换放入 process 块只是让最后一个帖子“通过” - Torbjörn Bergstedt
更新。您需要收集对象并传入结束块。其他方法可能使用代理函数,但这个方法要容易得多。 - stej
谢谢,期间收集输入到另一个数组 process { } 做了伎俩。但我仍然很想知道为什么行为会有所不同,具体取决于参数如何接收数据,对我来说它们应该以相同的方式运行。 - Torbjörn Bergstedt
我有更多的Powershell经验,我现在明白了不同之处:流水线将结果发送给了 process {} 块,而'标准'参数将它们发送到 end {} 块(这意味着如果你不使用任何一个函数的整个函数 begin{}, process {} 要么 end {} 块) - Torbjörn Bergstedt


如果要求高级功能,那么我会采用@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 在这种情况下,是管道输入的自动变量。


4
2018-02-17 15:16



这也有效(我之前尝试过 $input),但由于我想控制参数,@ stej上面的答案更适合我。 - Torbjörn Bergstedt


我认为问题是管道将数组展开到一个对象流中,并一次一个地呈现给你的函数,而不是作为数组。

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

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

2
2018-02-17 15:16





我遇到了同样的问题和问题,我通常解决这个问题的方式如下:

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

因为我不认为这是一个好主意 覆盖 该 自动$输入变量

无论如何,我还没有看到问题部分的答案:“有人可以解释一下吗?
我想这与它有关 强烈鼓励的发展指南 其中说明:

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

ProcessRecord 方法在我看来是一个 C# 我推测的方法是由 process 阻止在解决方案中 stej。但这并不能解释为什么这样做会这样 PSCustomObject 数组而不是例如系统对象,如:

Get-psdrive | Show-Data

甚至:

@(Get-psdrive) | Show-Data

1
2017-12-31 13:21