Search code examples
powershellobjectvariablesjobs

Counting returned objects from jobs in powershell?


quick question, I've got a short little script that creates 2 background jobs and pings some websites, now since jobs cannot return variables apparently I figured I'd try it as follows (which is probably a stupid way of doing it but okay). Now the weird thing is that it seems to work somewhat? But the count it returns is nowhere near what it should be, it's very random, sometimes it stops at 2, sometimes at 3, can anyone maybe tell me what I'm doing wrong? Shouldn't $i end up equalling 7? Initially I wanted parallel runspaces but I've been trying to wrap my head around that for weeks now and I've come to the conclusion that I'm too dumb for that apparently. Is what I'm trying to do here even possible?

$1 = @('google.com', 'bing.com', 'yahoo.com', 'startpage.com')
$2 = @('reddit.com', 'facebook.com', 'nonexistent.com')

function run {
  $pwd = Get-Location
  $i = 0

  $list = {
    function list1 ($1, $pwd) {
      foreach ($ping in $1) {
        "&"
        if (Test-Connection -ComputerName $ping -Count 1 -Quiet) {
          "$pwd\" + "$ping"              
        } 
      } 
    }
    function list2 ($2, $pwd) {

      foreach ($ping in $2) {
        "&"
        if (Test-Connection -ComputerName $ping -Count 1 -Quiet) {
          "$pwd\" + "$ping"
        } 
      } 
    }
  }

  echo "ping?`nyes or no`n"; $y = Read-Host
  if ($y -eq 'y') {
    Start-Job -ScriptBlock { list1 $using:1 $using:pwd } -InitializationScript $list
    Start-Job -ScriptBlock { list2 $using:2 $using:pwd } -InitializationScript $list

    While (Get-Job | Where-Object HasMoreData) {
      Get-Job | Where-Object HasMoreData | Receive-Job | Where-Object { $_ -notlike "&" } | Tee-Object -FilePath ping.txt -Append 
      Get-Job | Where-Object HasMoreData | Receive-Job | Where-Object { $_ -like "&" } | Foreach-Object { $i += 1 }
    }
    $i | Add-Content totalcount.txt
  }
}

Solution

  • Fix to the script is below.

    Everytime you use Receive-Job, it takes the data from stream and displays it. After displaying it, that data is no longer accessible via Receive-job. To fix this, either Use -Keep switch or store the data you get from Receive-Object in a variable to use it multiple times.

    if ($y -eq 'y') {
        Start-Job -ScriptBlock { list1 $using:1 $using:pwd } -InitializationScript $list
        Start-Job -ScriptBlock { list2 $using:2 $using:pwd } -InitializationScript $list
    
        While (Get-Job | Where-Object HasMoreData) {
          Get-Job | Where-Object HasMoreData | Receive-Job -Keep | Where-Object { $_ -notlike "&" } | Tee-Object -FilePath ping.txt -Append 
          Get-Job | Where-Object HasMoreData | Receive-Job | Where-Object { $_ -like "&" } | Foreach-Object { $i += 1 }
        }
        $i | Add-Content totalcount.txt
      }
    

    I would however recommend a little different route than what you have. Instead of getting the data while you are checking to see if the job completed, run the loop and output this way,

    if ($y -eq 'y') {
        Start-Job -ScriptBlock { list1 $using:1 $using:pwd } -InitializationScript $list
        Start-Job -ScriptBlock { list2 $using:2 $using:pwd } -InitializationScript $list
    
        While ((Get-Job | ? {$_.State -eq "Running" }).Count -gt 0) {
          # wait for all jobs to complete...
          Start-Sleep 1
        }
    
        #Once all the jobs are done, you can check the results.
        $data = Get-Job | Receive-Job | Where-Object { $_ -notlike "&" } 
        $data | Tee-Object -FilePath ping.txt -Append 
        $data.Count | Add-Content totalcount.txt
    
        Get-Job | Remove-Job # Dont forget to clean up.
    
      }