Поведение 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
Весь массив представлен, как и ожидалось. Может кто-то объяснить это - и, надеюсь, поможет мне исправить это?
Ответы
Ответ 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
Ответ 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)
N.B. $input
в этом случае является автоматической переменной для ввода конвейера.
Ответ 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}
...
Как я не думаю, что неплохо переписать автоматическую переменную $Input.
Во всяком случае, я не видел ответа на часть вопроса: "Кто-нибудь может объяснить это?"
Я полагаю, что это имеет какое-то отношение к Сильно поощряемым рекомендациям по разработке, в котором говорится:
Поддержка метода ProcessRecord
Чтобы принять все записи из предыдущего командлета в конвейере, ваш командлет должен реализовать метод ProcessRecord. Windows PowerShell вызывает этот метод несколько раз, один раз для каждой записи который отправляется вашему командлету.
Метод ProcessRecord
представляется мне как метод С#, который, как я полагаю, вызывается process
как в решении от stej. Но это не объясняет, почему это работает для массива PSCustomObject
, а не для, например, системные объекты, например:
Get-psdrive | Show-Data
Или даже:
@(Get-psdrive) | Show-Data