Question

I'm trying to get a better understanding of how powershell handles jobs.

Both of these article posts helped a lot:

However, with the following sample script, I am getting unexpected results while calling Receive-Job. I expect a result on each line, but sometimes I see multiple results per line or extra blank carriage returns. Any idea on how to write out just the information that streams in from the console from each job?

$loops = 1..10
$jobs = new-object System.Collections.ArrayList

$loops | % {
    $jobs.add( 
        (start-job -ScriptBlock {
            param($list)
            $list | % {
                sleep -seconds (get-random -Maximum 3 -Minimum 1)
                write-host "Number is: $_"
            }
        } -ArgumentList (,$loops))
    ) | out-null
}


while ($jobs.count -gt 0)
{
    if ($jobs -ne $null)
    {
        $list = $jobs | ? { $_.HasMoreData -eq $true }
        $list | % { Receive-Job -Job $_ }
        $list2 = $jobs.Clone() | ? { $_.State -eq "Completed" }
        $list2 | % {
            $jobs.Remove($_) | out-null
        }
        $list = $null
    }
}

Output can be...

Number is: 1
Number is: 1
Number is: 2

Or sometimes...

Number is: 1
Number is: 2
Number is: 1Number is: 2
Number is: 1Number is: 2Number is: 2
Was it helpful?

Solution

You are overcomplicating this by a lot. You don't need all of those loops to process things manually. You should take advantage of the cmdlets that are available to you. This should achieve the basics of what you want:

$loops = 1..10

$scriptBlock = {
  param($list)
  $list | % {
    sleep -seconds (get-random -Maximum 3 -Minimum 1)
    write-host "Number is: $_"
  }
}

$jobs = $loops | % { start-job -ScriptBlock $scriptBlock -ArgumentList (,$loops) }

$jobs | Wait-Job | Receive-Job

However, this will group all output together without any way to know what job did what, though. And the fact that you are using Write-Host means you can't actually access the data, it is merely printed to the screen.

If you want to actually store or process the resulting job output, you can do something like this:

$loops = 1..10

$scriptBlock = {
  param($list)
  $list | % {
    sleep -seconds (get-random -Maximum 3 -Minimum 1)
    "Number is: $_"  # don't use write-host, just output the string
  }
}

$jobs = $loops | % { start-job -ScriptBlock $scriptBlock -ArgumentList (,$loops) }

$jobs | Wait-Job  # wait for all jobs to be complete
$jobs |%{
   $output = $_ | Receive-Job
   # process $output here, do what you want with it
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top