1

I have:

for($i = 1 ; $i -le 3; $i++)
    {
        Start-Process powershell.exe
    }

but I don't know how I would make the new windows run a ping command. Could be done with an extra script but have no idea. Thanks

Itchydon
  • 2,572
  • 6
  • 19
  • 33
bigNurd
  • 13
  • 2
  • Why is it important that `ping` runs in multiple separate processes? – Mathias R. Jessen Jul 11 '22 at 11:38
  • To run multiple pings a time as you can’t do that in one window as far as I’m aware – bigNurd Jul 11 '22 at 11:43
  • Have a look at [Test-Connection](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.management/test-connection?view=powershell-7.2), which can 'ping' multiple computers at the same time. – boxdog Jul 11 '22 at 11:45
  • Can I use this to ping the same computer multiple times at a time – bigNurd Jul 11 '22 at 11:47
  • Also ping isn’t the only command but instead use for more general commands – bigNurd Jul 11 '22 at 11:52
  • To clarify: while `Test-Connection` accepts multiple host names / addresses to ping, they are pinged _one after the other_. – mklement0 Jul 11 '22 at 15:54

2 Answers2

0

Start-Process has an -ArgumentList parameter that can be used to pass arguments to the new PowerShell process.

powershell.exe and pwsh.exe have a -Command parameter that can be used to pass them a command.

You can combine the two like this:

for ($i = 1; $i -le 3; $i++) 
{ 
    Start-Process powershell.exe -ArgumentList '-NoExit',"-Command ping 127.0.0.$i" 
}

If you don't use the -NoExit parameter the window will close as soon as the ping command finishes.

As mentioned in the comments to the question it is also possible to ping multiple hosts using the Test-Connection command like this:

Test-Connection -TargetName 127.0.0.1,127.0.0.2

This has a downside though in that it seems to ping one after the other rather than doing it in parallel.

Another way to do much the same thing in parallel, and probably more PowerShell style is to use jobs:

$jobs = @()
for ($i = 1; $i -le 3; $i++)
{
    $jobs += Start-ThreadJob -ArgumentList $i { PARAM ($i)
        Test-Connection "127.0.0.$i"
    }
}
Wait-Job $jobs
Receive-Job $jobs -Wait -AutoRemoveJob

Note: Start-ThreadJob is newish. If you're still stuck on version 5 of PowerShell that comes with Windows use Start-Job instead, it spawns new processes where as Start-ThreadJob doesn't.

Nitpickers' corner

For those in the comments saying that appending to an array is slow. Strictly a more PowerShell way of doing this is given below. For three items, however, you won't be able to measure the difference and the readability of the code is way lower. It's also rather diverging from the original question.

1..3 | % { Start-ThreadJob -ArgumentList $_ { PARAM($i) Test-Connection "127.0.0.$i" } } | Wait-Job | Receive-Job -Wait -AutoRemoveJob
Martin Brown
  • 24,692
  • 14
  • 77
  • 122
  • 1
    There are multiple ways to do this, and I already felt I was getting carried away with the answer. I'll update the answer with your suggestions. – Martin Brown Jul 11 '22 at 14:38
  • Thanks for updating; to add a bit more info for future readers: The third-party [`Test-ConnectionAsync` and `Ping-Subnet`](https://github.com/proxb/AsyncFunctions/blob/master/Test-ConnectionAsync.ps1) functions use async .NET APIs (threads) for efficient parallel pinging. Another thread-based PowerShell (Core) 7+ option is [`ForEach-Object -Parallel`](https://learn.microsoft.com/powershell/module/microsoft.powershell.core/foreach-object); e.g.: `1..3 | ForEach-Object -Parallel { Test-Connection "127.0.0.$_" }` – mklement0 Jul 11 '22 at 15:42
  • Another aside: PowerShell allows you to use statements as _expressions_, so that you can do `$jobs = foreach ($i in 1..3) { "job$i" }`, for instance. Building an array iteratively with `+=` is not only more cumbersome, it is inefficient, because a _new_ array is created behind the scenes in every iteration - see [this answer](https://stackoverflow.com/a/60708579/45375). – mklement0 Jul 11 '22 at 15:52
  • @mklement0 if efficiency means that much, you would probably want to avoid using a scripting language and use C instead. – Martin Brown Jul 12 '22 at 19:59
  • While _in the abstract_ that is a valid point, and even _in this specific case_ it is (only 3 iterations), the more important point is that _casual, unqualified_ use of such a - PowerShell-_unidiomatic_ - approach can mislead future readers to believe that `+=` is a _generally_ valid technique for "growing" arrays - it is not, _even in the realm of scripting_ - compare the runtime of `$a=@(); foreach ($n in 1..1e4) { $a += $n }` to `$a = foreach ($n in 1..1e4) { $n }` and you'll see what I mean. – mklement0 Jul 12 '22 at 21:50
  • 1
    In short: Generally, I suggest not using un-idiomatic, inefficient solutions, unless accompanied by a _rationale_ for a deviation from what is customary. In the case at hand, there is no justification for the deviation at all, given that the idiomatic solution is both more efficient _and_ more concise. – mklement0 Jul 12 '22 at 21:50
0

Here's a pinger script I have to watch multiple computers.

# pinger.ps1

# example:  pinger comp001
# pinger $list

param ($hostnames)

#$pingcmd = 'test-netconnection -port 515'
$pingcmd = 'test-connection'

$sleeptime = 1

$sawup = @{}
$sawdown = @{}

foreach ($hostname in $hostnames) { 
  $sawup[$hostname] = $false
  $sawdown[$hostname] = $false
}

#$sawup = 0
#$sawdown = 0

while ($true) {
  # if (invoke-expression "$pingcmd $($hostname)") {

  foreach ($hostname in $hostnames) {
    if (& $pingcmd -count 1 $hostname -ea 0) {
      if (! $sawup[$hostname]) {
        echo "$([console]::beep(500,300))$hostname is up $(get-date)"
#        [pscustomobject]@{Hostname = $hostname; Status = 'up'; Date = get-date} # format-table waits for 2 objects
        $sawup[$hostname] = $true
        $sawdown[$hostname] = $false
      }
    } else {
      if (! $sawdown[$hostname]) {
        echo "$([console]::beep(500,300))$hostname is down $(get-date)"
#        [pscustomobject]@{Hostname = $hostname; Status = 'down'; Date = get-date}
        $sawdown[$hostname] = $true
        $sawup[$hostname] = $false
      }
    }
  }
  
  sleep $sleeptime
}

Example usage (it beeps):

.\pinger comp001,comp002

comp001 is up 07/13/2022 12:07:59
comp002 is up 07/13/2022 12:08:00
js2010
  • 23,033
  • 6
  • 64
  • 66