Как захватить исключение, поднятое в скриптблоке старта?
У меня есть следующий 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
Однако исключение, созданное в другом экземпляре powershell, невозможно захватить. Какой лучший способ захватить исключение?
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
Ответы
Ответ 1
Использование throw
изменит свойство объекта State
объекта задания на "Failed". Ключ должен использовать объект задания, возвращенный из Start-Job
или Get-Job
, и проверить свойство State
. Затем вы можете получить доступ к сообщению об исключении из самого объекта задания.
По вашему запросу я обновил пример, чтобы включить 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
}
}
Ответ 2
Это должен быть комментарий на самом деле, но у меня нет репутации, чтобы оставлять комментарии.
Отвечаю, что вы должны использовать ответ Andy Arismendi, но также вывести $job.ChildJobs[0].Error
Как $job.ChildJobs[0].JobStateInfo.Reason.Message
не всегда полезно.
Ответ 3
Я смог "реконструировать" исключение в основном потоке, используя:
Receive-Job $job -ErrorAction Stop
В качестве примера я воспользуюсь случаем. Его можно легко применить к 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
Работает в версии 2.0.
Обратите внимание, что если ошибка в задании не завершается, последующие строки будут продолжать выполняться. Но это не будет очевидно в результатах, возвращаемых из Receive-Job, поскольку Receive-Job "завершает половину пути" - он выкидывает из себя сам, когда встречается объект ошибки.
Один из способов избежать этого - обернуть весь блок в try {} catch {throw;}
Кроме того, состояние задания не будет "Failed", если исключение не завершено