How to capture the exception raised in the scriptb

2020-02-09 01:33发布

I have the following script,

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

try {
    Start-Job -ScriptBlock $createZip -ArgumentList "abd", "acd"  
    echo "**Don't reach here if error**"
    LogThezippedFile
}
catch {
    echo "Captured: "
    $_ | fl * -force
}
Get-Job | Wait-Job 
Get-Job | receive-job 
Get-Job | Remove-Job 

However, the exception raised in another powershell instance cannot be captured. What's the best way to capture the exception?

Id              Name            State      HasMoreData     Location             Command                  
--              ----            -----      -----------     --------             -------                  
343             Job343          Running    True            localhost            ...                      
**Don't reach here if error**
343             Job343          Failed     True            localhost            ...                      
zip: abd
     --> acd
Receive-Job : test
At line:18 char:22
+ Get-Job | receive-job <<<<  
    + CategoryInfo          : OperationStopped: (test:String) [Receive-Job], RuntimeException
    + FullyQualifiedErrorId : test

3条回答
Lonely孤独者°
2楼-- · 2020-02-09 01:52

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.

查看更多
别忘想泡老子
3楼-- · 2020-02-09 02:01

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

查看更多
萌系小妹纸
4楼-- · 2020-02-09 02:03

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 
    }
}
查看更多
登录 后发表回答