In PowerShell, I have an array of objects that I need to pass to a function. The function is to then loop through all of the objects in the array, but it seems that it is not accepting the parameter value correctly.
Take the following example, where I pass an array containing two objects. I would expect the count of the array to be 2 both before the function and within the function, but as soon as it hits the function the count is 1, and my input is not as expected; only the last object is discovered.
Am I missing something here, or is this a bug in PowerShell?
### I've also tries '[object]', '[array]' and '[array[]]' as the type for '$testArr'.
function Test-PassArrayOfObjects
{
param(
[parameter(Mandatory,ValueFromPipeline)]
[object[]]$testArr
)
Write-Host "In function count: $($testArr.Count)"
$testArr | ForEach-Object { $_ }
}
$test1 = New-Object –TypeName PSObject
$test1 | Add-Member -MemberType NoteProperty -Name Test1 -Value Value1
$test2 = New-Object –TypeName PSObject
$test2 | Add-Member -MemberType NoteProperty -Name Test2 -Value Value2
$testArr = @($test1, $test2)
$testArr.GetType() | Format-Table
Write-Host "Before function count: $($testArr.Count)"
$testArr | Test-PassArrayOfObjects
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array
Before function count: 2
In function count: 1
Test2
-----
Value2
Based on the answer below, I have this working example, which I've been able to apply to my real life scenario.
function Test-PassArrayOfObjects
{
param(
[parameter(Mandatory,ValueFromPipeline)]
[object]$testArr
)
Process {
Write-Host "In function count: $($testArr.Count)"
$testArr
}
}
$test1 = New-Object –TypeName PSObject
$test1 | Add-Member -MemberType NoteProperty -Name Test1 -Value Value1
$test1 | Add-Member -MemberType NoteProperty -Name Test2 -Value Value2
$test2 = New-Object –TypeName PSObject
$test2 | Add-Member -MemberType NoteProperty -Name Test1 -Value Value1
$test2 | Add-Member -MemberType NoteProperty -Name Test2 -Value Value2
$testArr = @($test1, $test2)
$testArr.GetType() | Format-Table
Write-Host "Before function count: $($testArr.Count)"
$testArr | ForEach-Object { $_ | Test-PassArrayOfObjects }
When sending input to a function via the pipeline, your function should include a Process block:
function Test-PassArrayOfObjects
{
param(
[parameter(Mandatory,ValueFromPipeline)]
[object[]]$testArr
)
Process {
Write-Host "In function count: $($testArr.Count)"
$testArr | ForEach-Object { $_ }
}
}
This is necessary because of the way the pipeline handles collections I believe. It automatically unrolls them and handles them one item at a time, so your ForEach-Object isn't getting the whole collection in the $testArr variable.
You often see functions like this still incorporate a ForEach though, in case the input is sent via a parameter in which case it is received all at once. For example: Test-PassArrayOfObjects -TestArr @(1,2,3).
Your issue is further conflated by the fact that your array has two objects with different properties. This is creating confusion in the output because PowerShell decides how to format the output based on the first object and uses the same formatting when it outputs the second object, but then you don't see it because it doesn't share any of the same properties (I think this is what is occurring anyway..).
You can see that both objects get processed by putting | Format-List on the $_ which forces both outputs to be formatted as list output independently. Note that this isn't good practice in a real function scenario of course. An alternative is to make the property name on both objects Test1. Then you will see the output you probably expected without using Format-List.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With