1

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...

1
  • Well, the arrays within $arrPing will have two data values within them, but it is a dynamic array so technically this would be classed as jagged I suppose. Ideally, I just want the data to be paired together - this script is intended to be run on boot for thousands of computers, and it will take this data and attempt to run some FTP commands to retrieve a file. This data will be used to filter out computers which are not on the network (for various reasons) or are turned off so displaying the output in a table format is not important. Commented Nov 19, 2015 at 12:14

2 Answers 2

3

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.

Sign up to request clarification or add additional context in comments.

4 Comments

Should probably add a link to about_splatting
Hi @DarkLite1, I'll take a look at splatting as I've not seen or heard this technique being used before. Thanks :)
@Fredulom It is great for repeated calls to the same cmdlet! Or if you need to make dynamic calls to the same cmdlet. $TestParams.Count = 3 for instance.
You're welcome :) Matt is right, if you would like to do a lot of calls with Test-Connection, you can easily reuse the @TestParams. Or even update something in the $TestParams before using it again, like $TestParams.Entry = 'SERVER6'
2

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

4 Comments

And if I wish to add this to a variable to the 2nd or 3rd code sections in your response (instead of exporting the results to a .csv file) then I would simply add "$[VARIABLE_NAME]= " to the beginning of this and remove the "Export-Csv" command, correct?
@Fredulom Oh. I see now. Yes. That is all you have to do. It is just a hashtable @{} of key value pairs. So if you want you can look those up. You should not need the $ for declaring the key. Just make sure that all objects have the same property sets or they will not be grouped properly.
Awesome, that's great :) Thanks for your help @Matt!
@Fredulom I got it after you made updates. Read my comment now. You were updating as I was responding.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.