Fredulom
Fredulom

Reputation: 928

PowerShell - Not creating Jagged Array within forEach loop

So, I'm having an issue enumerating through a forEach loop in PowerShell (v3) and adding the variable being evaluated, as well as a Test-Connection result into an array. I'm trying to make $arrPing a multi-dimensional array as this will make it easier for me to filter and process the objects in there later in the script, but I'm encountering issues with the code.

My code looks like the following:

$arrPing= @();
$strKioskIpAddress= (Get-WmiObject Win32_NetworkAdapterConfiguration | Where-Object { $_.IPAddress -ne $null }).ipaddress

...FURTHER DOWN THE CODE...

$tmpIpAddress= Select-Xml -Path $dirKioskIpAddresses -XPath '//kiosks/kiosk' | Select-Object -ExpandProperty Node

forEach ( $entry in $tmpIpAddress )
    {
    if ( $entry -ne $strKioskIpAddress )
        {
        $result= Test-Connection -ComputerName $entry -Count 1 -BufferSize 16 -Quiet -ErrorAction SilentlyContinue
        $arrPing+= @($entry,$result);
        }
    }

But I'm getting the following output when I display the contents of the $arrPing variable:

PS H:\Documents\PowerShell Scripts> $arrPing
10.216.1.134
True
10.216.1.139
True
10.216.23.230
True
10.216.23.196
False
10.216.23.23
False

Can anyone tell me where I'm going wrong? I have a feeling that this is happening because I'm in a forEach loop but I just can't say for sure...

Upvotes: 1

Views: 340

Answers (2)

Matt
Matt

Reputation: 46710

You are seeing how PowerShell unrolls arrays. The variable is as designed: a large array. However PowerShell, when displaying those, puts each element on its own line. If you do not want that and especially if you are going to use This data will be used to filter out computers which are not on the network then you should use PowerShell objects.

if ( $entry -ne $strKioskIpAddress ){
    $objPing += New-Object -TypeName psobject -Property @{
        Entry = $entry
        Result = Test-Connection -ComputerName $entry -Count 1 -BufferSize 16 -Quiet -ErrorAction SilentlyContinue
    }
}

Instead of that those I would continue and use a different foreach contruct which is more pipeline friendly. That way you can use other cmdlets like Export-CSV if you need this output in other locations. Also lie PetSerAl says

[Y]ou should not use array addition operator and add elements one by one. It [will] create [a] new array (as arrays are not resizable) and copy elements from [the] old one on each operation.

$tmpIpAddress | Where-Object{$_ -ne $strKioskIpAddress} | ForEach-Object{
    New-Object -TypeName psobject -Property @{
        Entry = $_
        Result = Test-Connection -ComputerName $_ -Count 1 -BufferSize 16 -Quiet -ErrorAction SilentlyContinue
    }
} | Export-CSV -NoTypeInformation $path

The if is redundant now that we have moved that logic into Where-Object since you were using it do filter out certain records anyway. That is what Where-Object is good for.

The above code is good for PowerShell 2.0. If you have 3.0 or later then use [pscutomobject] and [ordered]

$tmpIpAddress | Where-Object{$_ -ne $strKioskIpAddress} | ForEach-Object{
    [psobject][ordered] @{
        Entry = $_
        Result = Test-Connection -ComputerName $_ -Count 1 -BufferSize 16 -Quiet -ErrorAction SilentlyContinue
    }
} | Export-CSV -NoTypeInformation $path

Upvotes: 2

DarkLite1
DarkLite1

Reputation: 14705

I would simplify it a bit by using a PSCustomObject:

$Ping = foreach ($Entry in $tmpIpAddress) {
    if ($Entry -ne $strKioskIpAddress) {
        $TestParams = @{
            ComputerName = $Entry 
            Count        = '1'
            BufferSize   = '16'
            Quiet        = $true
            ErrorAction  = 'SilentlyContinue'
        }
        $Result = Test-Connection @TestParams
        [PSCustomObject]@{
            Entry = $Entry
            Result = $Result
        }
    }
}

$Ping

To avoid a long row of parameters I've used a technique called splatting.

Upvotes: 3

Related Questions