How to capture the exception raised in the scriptblock of start-job?

前端 未结 4 531
说谎
说谎 2020-12-29 23:08

I have the following script,

$createZip = {
    Param ([String]$source, [String]$zipfile)
    Process { 
        echo \"zip: $source`n     --> $zipfile\"
         


        
相关标签:
4条回答
  • 2020-12-29 23:20

    This should be a comment really, but I don't have the reputation to leave comments.

    My answer is that you should use Andy Arismendi's answer, but also output $job.ChildJobs[0].Error

    As $job.ChildJobs[0].JobStateInfo.Reason.Message isn't always useful.

    0 讨论(0)
  • 2020-12-29 23:23

    TLDR:

    # Works with both terminating and non terminating errors
    $j = start-job {1/0} | wait-job; try { rcjb $j -ErrorAction Stop } catch { "err $_" } 
    
    
    0 讨论(0)
  • 2020-12-29 23:40

    I was able to "rethrow" the exception in the main thread by using:

    Receive-Job $job -ErrorAction Stop
    

    I'll my use case as an example. It can easily be applied to the OP.

    $code = {
        $Searcher = New-Object -ComObject Microsoft.Update.Searcher
        #Errors from Search are not terminating, but will be present in the output none the less.
        $Results = $Searcher.Search('IsInstalled=0  and IsHidden=0')
        $Results.Updates
    };
    $job = Start-Job -ScriptBlock $code;
    $consume = Wait-Job $job -Timeout 600;
    
    if ($job.state -eq 'Running') {
        Stop-Job $job
        throw 'Windows update searcher took more than 10 minutes. Aborting' 
    };
    
    #Captures and throws any exception in the job output
    Receive-Job $job -ErrorAction Stop;
    Write-Host "Finished with no errors"; #this will not print if there was an error
    

    Works in v2.0.

    Note that if the error within the job is non-terminating, the subsequent lines will continue to execute. But, this will not be obvious in the output returned from Receive-Job, as Receive-Job "terminates half way thorugh" - it throws out of it's self when the error object is encountered.

    One way to avoid that is to wrap the whole block in a try {} catch{throw;}

    Also, Job state will not be 'Failed' if the exception is non-terminating

    0 讨论(0)
  • 2020-12-29 23:43

    Using throw will change the job object's State property to "Failed". The key is to use the job object returned from Start-Job or Get-Job and check the State property. You can then access the exception message from the job object itself.

    Per your request I updated the example to also include concurrency.

    $createZip = {
        Param ( [String] $source, [String] $zipfile )
    
        if ($source -eq "b") {
            throw "Failed to create $zipfile"
        } else {
            return "Successfully created $zipfile"
        }
    }
    
    $jobs = @()
    $sources = "a", "b", "c"
    
    foreach ($source in $sources) {
        $jobs += Start-Job -ScriptBlock $createZip -ArgumentList $source, "${source}.zip"
    }
    
    Wait-Job -Job $jobs | Out-Null
    
    foreach ($job in $jobs) {
        if ($job.State -eq 'Failed') {
            Write-Host ($job.ChildJobs[0].JobStateInfo.Reason.Message) -ForegroundColor Red
        } else {
            Write-Host (Receive-Job $job) -ForegroundColor Green 
        }
    }
    
    0 讨论(0)
提交回复
热议问题