I'm making some modules to make RESTAPI calls easier for our software (since all resources use different logic to how I can find them and how they should be called but that's a separate story). I'm making Get-, Set-, New-, and Remove- for each resource and just found that you can't pipe more than one object to modules that takes arrays from the pipeline. Here's an illustration of the problem that you can copy-paste into your environment:
function Get-MyTest {
param(
[string[]]$MyInput
)
[array]$Output = @()
$i=0
foreach($Item in $MyInput){
$i
$Output = [pscustomobject]@{
Name = $Item
Id = $i
}
}
return $Output
}
function Get-IncorrectResults {
param(
[parameter(ValueFromPipeline)][array]$MyIncorrectInput
)
foreach ($Item in $MyIncorrectInput) {
Write-Host "Current object: $Item `n "
}
}
The Get module can return more than one object. Each object returned is a PSCustomObject
, so if there are more than one returned it becomes an array of PSCustomObjects
. Here's the problem:
This works:
$Results = Get-MyTest -MyInput "Test1","Test2","Test3"
Get-IncorrectResults -MyIncorrectInput $Results
This only returns the first item:
$Results = Get-MyTest -MyInput "Test1","Test2","Test3"
$Results | Get-IncorrectResults
If the Get part returns more than one object, only the first object is passed on to the Remove-MyModule
. I tried changing the parameter definition from [pscustomobject[]]
to [array]
but it's the same result. What is the reason for dropping all but the first array item when piping but not when using it as a parameter?
CodePudding user response:
The structure of an advanced PowerShell function is as follows:
function Function-Name {
param(
<# parameter definitions go here#>
)
begin {
<# this runs once when the pipeline starts #>
}
process {
<# this runs once for every input item piped to the function #>
}
end {
<# this runs once at the end of the pipeline #>
}
}
When you omit the begin
/process
/end
blocks, PowerShell assumes your function body is really the end
block - so this function definition:
function Function-Name {
param()
Do-Something
}
... is the exact same as:
function Function-Name {
param()
begin {}
process {}
end {Do-Something}
}
Notice that the process
block - the only piece of the function body that repeats with new input - is assumed empty, and the Do-Something
statement will not be executed until after the last input item has been received.
This is why it looks like it "drops" everything but the last input object.
Place the code that consumes the pipeline-bound variable inside the process
block and it'll work:
Remove-MyModule{
param(
[parameter(ValueFromPipeline)][pscustomobject[]]$MyModules
)
process {
foreach($Module in $MyModules){
Invoke-RestMethod -Method Delete -Uri "MyURL" -Body (ConvertTo-Json $Module) -UseDefaultCredentials
}
}
}
For more information about the syntax and semantics of advanced functions, see the about_Functions_Advanced_Methods
help file