#powershell #error-handling #powershell-4.0
#powershell #обработка ошибок #powershell-4.0
Вопрос:
Я пытаюсь автоматизировать задачу, которая выполняется путем ручного вызова подчиненного скрипта, который я не хочу изменять больше, чем необходимо, и должен оставить возможность использовать его вручную.
До сих пор мне удалось создать вызывающий скрипт, который выполняет то, что мы привыкли делать вручную, но я не могу найти элегантный способ завершить конвейер при первой ошибке завершения.
Идея заключается в том, что если ошибка завершения встречается где-либо в главном или подчиненном, весь конвейер завершается со статусом ошибки.
Это самое дальнее, что я получил:
PipelineMaster.ps1
$groups = (@('server1', 'server2', 'server3'), @('server4'))
trap {
'trap at master';
if ($_.FullyQualifiedErrorId -ne 1) { Write-Error -ErrorRecord $_ }
throw 1
}
ForEach ($group in $groups) {
Invoke-Command -ComputerName $group[0] -ArgumentList $group -ScriptBlock {
param([Parameter(ValueFromRemainingArguments = $true)][String[]]$group)
trap {
'trap at invocation';
if ($_.FullyQualifiedErrorId -ne 1) { Write-Error -ErrorRecord $_ }
throw 1
}
# preparation per group
.PipelineSlave.ps1 -group $group
}
}
PipelineSlave.ps1:
param([string[]]$group)
trap {
'trap at slave';
if ($_.FullyQualifiedErrorId -ne 1) { Write-Error -ErrorRecord $_ }
throw 1
}
ForEach ($h in $group) {
Invoke-Command -ComputerName $h -ScriptBlock {
trap {
'trap at node';
if ($_.FullyQualifiedErrorId -ne 1) { Write-Error -ErrorRecord $_ }
throw 1
}
" ## $(hostname)"
"minor err"; New-Item -ItemType Directory -Path c:
'major err'; New-Item -ItemType Directory -Path c:temp -ErrorAction Stop
" ## Done on $(hostname)"
}
}
Это используется trap
на каждом уровне области.
Я экспериментировал с $? после завершения каждой области, но это также срабатывает при не завершающихся ошибках, и я не нашел способа отличить.
Другим вариантом было просто обернуть все в try
и throw 1
после получения там. В качестве побочного вопроса: почему try{...} catch {throw}
работает по-другому от try{...} catch {throw 1}
? Первый завершает только контекст, последний — весь конвейер, и я не смог найти ни одной статьи, в которой это объяснялось.
Наконец, я также экспериментировал с этим (упрощено здесь):
ForEach ($group in $groups) {
Invoke-Command -ComputerName $group[0] -ArgumentList $group -ScriptBlock {
ForEach ($h in $group) {
Invoke-Command ComputerName $h -ScriptBlock {
New-Item -ItemType Directory -Path C:
New-Item -ItemType Directory -Path C:temp
if (!$?) { return 'ERROR' }
"Done on $(hostname)"
} | % {if ('ERROR' -eq $_) {return 'ERROR'} else {$_}}
}
} | % {if ('ERROR' -eq $_) {return 'ERROR'} else {$_}}
}
Это сделало то, что я хотел (в основном), но вместо того, чтобы полагаться на собственное разграничение PS между завершающими и не завершающими ошибками, мне нужно убедиться, что ошибка не завершается, а затем проверить результат каждой команды, которую я хочу завершить.
Я понимаю, что я мог бы также сделать выше, но throw 1
вместо return 'ERROR'
этого, но это очень похоже на первый подход и более утомительно.
Примечание: Команды не обязательно являются новыми элементами, но они являются надежным способом запуска как завершающих, так и не завершающих ошибок в PS.
Комментарии:
1. Вы хотите, чтобы ошибки завершения… завершить? Это то, что они делают. Просто удалите ловушки. Или, пожалуйста, уточните вашу проблему.
2. @marsze Да… Если я удалю ловушки, они завершат работу только среды, а не всего конвейера, поэтому в случае ошибки завершения на «server2» выполнение будет следующим: «запуск на server1: OK» «запуск на server2: ОШИБКА, прервана» «запуск на server4 …» Здорово, что он пропускает ‘server3’, но все равно продолжает ‘server4’ — это моя проблема.