1

Hello all and good afternoon!

I had a quick question regarding -asjob running with invoke-command. If I run 2 Invoke-Command's using -asjob, does it run simultaneously when I try to receive the ouput? Does this mean wait-job waits till the first job specified is finished running to get the next results?

Write-Host "Searching for PST and OST files.  Please be patient!" -BackgroundColor White -ForegroundColor DarkBlue
$pSTlocation = Invoke-Command -ComputerName localhost -ScriptBlock {Get-Childitem "C:\" -Recurse -Filter "*.pst" -ErrorAction SilentlyContinue | % {Write-Host $_.FullName,$_.lastwritetime}} -AsJob
$OSTlocation = Invoke-Command -ComputerName localhost -ScriptBlock {Get-Childitem "C:\Users\me\APpdata"  -Recurse -Filter "*.ost" -ErrorAction SilentlyContinue | % {Write-Host $_.FullName,$_.lastwritetime} } -AsJob


$pSTlocation | Wait-Job | Receive-Job 
$OSTlocation | Wait-Job | Receive-Job 

Also, another question: can i save the output of the jobs to a variable without it showing to the console? Im trying to make it where it checks if theres any return, and if there is output it, but if theres not do something else.

I tried:

$job1 = $pSTlocation | Wait-Job | Receive-Job 
    if(!$job1){write-host "PST Found: $job1"} else{ "No PST Found"}   

$job2 = $OSTlocation | Wait-Job | Receive-Job 
    if(!$job2){write-host "OST Found: $job2"} else{ "No OST Found"}    

No luck, it outputs the following:

enter image description here

enter image description here

Abraham Zinala
  • 4,267
  • 3
  • 9
  • 24
  • have you taken a look at what is in `$pSTlocation`? i suspect that whatever is there is NOT a reference to a job ... and that it isn't piping anything to `Wait-Job`. – Lee_Dailey Jan 27 '21 at 21:05
  • Hi Lee, when running the selection of `$pSTlocation`, its registered as a job with the ID, Name, State, etc. What you're seeing in the snippet is the actual output of the `$pSTlocation`, and `$OSTlocation`. – Abraham Zinala Jan 27 '21 at 21:08
  • i presume by "snippet" you are referring to that nasty, icky _image_ of text? [*grin*] that just shows the file names that were found. what does does `Get-Member` say is in the `$pSTlocation` var? what is its type? – Lee_Dailey Jan 27 '21 at 21:54
  • Right, cause that's what output of `$pSTlocation | Wait-Job | Receive-Job` is lol just shows it is grabbing the job, just attached the`$pSTlocation` members class. – Abraham Zinala Jan 27 '21 at 22:06
  • 1
    please, don't post images of code/errors/data. why? lookee ... Why not upload images of code/errors when asking a question? - Meta Stack Overflow — https://meta.stackoverflow.com/questions/285551/why-not-upload-images-of-code-errors-when-asking-a-question – Lee_Dailey Jan 27 '21 at 23:18
  • 1
    i see that you are trying to run parallel code on _remote systems_. you DO NOT need the `-AsJob` to do that. in fact, you are slowing things down. the proper way is to use the `-ComputerName` parameter and _give it an array of computer names. that runs the scriptblock on the remote systems in parallel. – Lee_Dailey Jan 27 '21 at 23:19
  • @Lee_Dailey, good point in general, but the two aren't mutually exclusive: definitely always pass all computer names as an _array_ to `-ComputerName` to execute on multiple machines, but you may still opt to receive _jobs_ as the result with `-AsJob`, so as to continue to perform processing on the local machine while the remote commands run, before later checking for completion of the remote commands. – mklement0 Jan 27 '21 at 23:34

2 Answers2

3

Note: This answer does not directly answer the question - see the other answer for that; instead, it shows a reusable idiom for a waiting for multiple jobs to finish in a non-blocking fashion.


The following sample code uses the child-process-based Start-Job cmdlet to create local jobs, but the solution equally works with local thread-based jobs created by Start-ThreadJob as well as jobs based on remotely executing Invoke-Command -ComputerName ... -AsJob commands, as used in the question.

It shows a reusable idiom for a waiting for multiple jobs to finish in a non-blocking fashion that allows for other activity while waiting, along with collecting per-job output in an array.

Here, the output is only collected after each job completes, but note that collecting it piecemeal, as it becomes available, is also an option, using (potentially multiple) Receive-Job calls even before a job finishes.

# Start two jobs, which run in parallel, and store the objects
# representing them in array $jobs.
# Replace the Start-Job calls with your 
#   Invoke-Command -ComputerName ... -AsJob
# calls.
$jobs = (Start-Job { Get-Date; sleep 1 }), 
        (Start-Job { Get-Date '1970-01-01'; sleep 2 })

# Initialize a helper array to keep track of which jobs haven't finished yet.
$remainingJobs = $jobs

# Wait iteratively *without blocking* until any job finishes and receive and 
# output its output, until all jobs have finished.
# Collect all results in $jobResults.
$jobResults = 
  while ($remainingJobs) {

    # Check if at least 1 job has terminated.
    if ($finishedJob = $remainingJobs | Where State -in Completed, Failed, Stopped, Disconnected | Select -First 1) {

      # Output the just-finished job's results as part of custom object
      # that also contains the original command and the 
      # specific termination state.
      [pscustomobject] @{
        Job    = $finishedJob.Command
        State  = $finishedJob.State
        Result = $finishedJob | Receive-Job
      }

      # Remove the just-finished job from the array of remaining ones...
      $remainingJobs = @($remainingJobs) -ne $finishedJob
      # ... and also as a job managed by PowerShell.
      Remove-Job $finishedJob

    } else {

      # Do other things...
      Write-Host . -NoNewline
      Start-Sleep -Milliseconds 500

    }

  }

# Output the jobs' results
$jobResults

Note:

  • It's tempting to try $remainingJobs | Wait-Job -Any -Timeout 0 to momentarily check for termination of any one job without blocking execution, but as of PowerShell 7.1 this doesn't work as expected: even already completed jobs are never returned - this appears to be bug, discussed in GitHub issue #14675.
mklement0
  • 382,024
  • 64
  • 607
  • 775
1

If I run 2 Invoke-Command's using -asjob, does it run simultaneously when I try to receive the output?

Yes, PowerShell jobs always run in parallel, whether they're executing remotely, as in your case (with Invoke-Command -AsJob, assuming that localhost in the question is just a placeholder for the actual name of a different computer), or locally (using Start-Job or Start-ThreadJob).

However, by using (separate) Wait-Job calls, you are synchronously waiting for each jobs to finish (in a fixed sequence, too). That is, each Wait-Job calls blocks further execution until the target job terminates.[1]

Note, however, that both jobs continue to execute while you're waiting for the first one to finish.

If, instead of waiting in a blocking fashion, you want to perform other operations while you wait for both jobs to finish, you need a different approach, detailed in the the other answer.

can i save the output of the jobs to a variable without it showing to the console?

Yes, but the problem is that in your remotely executing script block ({ ... }) you're mistakenly using Write-Host in an attempt to output data.

Write-Host is typically the wrong tool to use, unless the intent is to write to the display only, bypassing the success output stream and with it the ability to send output to other commands, capture it in a variable, or redirect it to a file. To output a value, use it by itself; e.g., $value instead of Write-Host $value (or use Write-Output $value, though that is rarely needed); see this answer.

Therefore, your attempt to collect the job's output in a variable failed, because the Write-Host output bypassed the success output stream that variable assignments capture and went straight to the host (console):

# Because the job's script block uses Write-Host, its output goes to the *console*,
# and nothing is captured in $job1
$job1 = $pSTlocation | Wait-Job | Receive-Job 

(Incidentally, the command could be simplified to $job1 = $pSTlocation | Receive-Job -Wait).


[1] Note that Wait-Job has an optional -Timeout parameter, which allows you to limit waiting to at most a given number of seconds and return without output if the target job hasn't finished yet. However, as of PowerShell 7.1, -Timeout 0 for non-blocking polling for whether jobs have finished does not work - see GitHub issue #14675.

mklement0
  • 382,024
  • 64
  • 607
  • 775