Когда я должен использовать Write-Error против Throw? Завершение и не прекращение ошибок - PullRequest
125 голосов
/ 15 февраля 2012

Глядя на скрипт Get-WebFile в PoshCode, http://poshcode.org/3226, Я заметил это странное для меня устройство:

$URL_Format_Error = [string]"..."
Write-Error $URL_Format_Error
return

В чем причина этого, а не в следующем?

$URL_Format_Error = [string]"..."
Throw $URL_Format_Error

Или даже лучше:

$URL_Format_Error = New-Object System.FormatException "..."
Throw $URL_Format_Error

Как я понимаю, вы должны использовать Write-Error для нескончаемых ошибок и Throw для завершающих ошибок, так что мне кажетсячто вы не должны использовать Write-Error с последующим возвратом.Есть ли разница?

Ответы [ 6 ]

161 голосов
/ 15 февраля 2012

Write-Error следует использовать, если вы хотите сообщить пользователю о некритической ошибке. По умолчанию все, что он делает, это печатает сообщение об ошибке красным текстом на консоли. Это не останавливает конвейер или цикл от продолжения. Throw, с другой стороны, выдает то, что называется завершающей ошибкой. Если вы используете команду throw, конвейер и / или токовая петля будут прерваны. Фактически все выполнение будет прекращено, если вы не используете структуру trap или try/catch для обработки ошибки завершения.

Следует отметить одну вещь, если вы установите $ErrorActionPreference на "Stop" и используете Write-Error, это приведет к завершающей ошибке .

В скрипте, на который вы ссылались, мы находим следующее:

if ($url.Contains("http")) {
       $request = [System.Net.HttpWebRequest]::Create($url)
}
else {
       $URL_Format_Error = [string]"Connection protocol not specified. Recommended action: Try again using protocol (for example 'http://" + $url + "') instead. Function aborting..."
       Write-Error $URL_Format_Error
    return
   }

Похоже, что автор этой функции хотел остановить выполнение этой функции и отобразить сообщение об ошибке на экране, но не хотел, чтобы весь скрипт прекратил выполнение. Автор сценария мог бы использовать throw, однако это означало бы, что при вызове функции вам придется использовать try/catch.

return выйдет из текущей области, которая может быть функцией, сценарием или блоком сценария. Это лучше всего иллюстрируется кодом:

# A foreach loop.
foreach ( $i in  (1..10) ) { Write-Host $i ; if ($i -eq 5) { return } }

# A for loop.
for ($i = 1; $i -le 10; $i++) { Write-Host $i ; if ($i -eq 5) { return } }

Вывод для обоих:

1
2
3
4
5

Один гоча здесь использует return с ForEach-Object. Это не нарушит обработку, как можно было бы ожидать.

Дополнительная информация:

15 голосов
/ 15 февраля 2012

Основное различие между командлетом Write-Error и ключевым словом throw в PowerShell состоит в том, что первое просто печатает некоторый текст по стандарту поток ошибок (stderr) , в то время как последний фактически завершает обработку выполняемой команды или функции, которая затем обрабатывается PowerShell путем отправки информации об ошибке в консоль.

Вы можете наблюдать различное поведение двух в приведенных вами примерах:

$URL_Format_Error = [string]"..."
Write-Error $URL_Format_Error
return

В этом примере ключевое слово return было добавлено в явно , чтобы остановить выполнение сценария после отправки сообщения об ошибке на консоль. Во втором примере, с другой стороны, ключевое слово return необязательно, поскольку завершение неявным образом выполняется с помощью throw:

$URL_Format_Error = New-Object System.FormatException "..."
Throw $URL_Format_Error
11 голосов
/ 10 октября 2016

Важно : существует 2 типов завершающих ошибок , к которым текущие разделы справки, к сожалению, сопоставляют :

  • оператор , определяющий ошибки, сообщаемые командлетами в определенных невосстанавливаемых ситуациях и выражениями, в которых возникает исключение .NET / ошибка времени выполнения PS; только оператор завершается, а выполнение сценария по умолчанию продолжается .

  • скрипт , определяющий ошибки, вызванные либо Throw, либо эскалацией одного из других типов ошибок через переменную предпочтения-действия-ошибки / значение параметра Stop.
    Если они не перехвачены, они завершают текущий поток выполнения (т. Е. Не только текущий скрипт, но и всех его вызывающих, если это применимо).

Для полного обзора обработки ошибок PowerShell см. эту проблему документации GitHub .

Остальная часть этого поста посвящена неразрывным против завершающих операторов ошибок.


В дополнение к существующим полезным ответам с акцентом на суть вопроса: Как вы выбираете , следует ли сообщать о выписке - прекращается или не ошибка ?

Отчет об ошибках командлета содержит полезные рекомендации; позвольте мне попробовать прагматическое резюме :

Общая идея не прекращающихся ошибок состоит в том, чтобы разрешить "отказоустойчивую" обработку больших входных наборов : отказ обрабатывать подмножество входных объектов не должен (по по умолчанию) прервать - потенциально длительный - процесс в целом, что позволяет проверять ошибки и обрабатывать только объекты с ошибками позже - как сообщается в записях ошибок, собранных в автоматическом режиме переменная $Error.

  • Сообщить об ошибке НЕ ПРЕКРАЩЕНИЯ , если ваш командлет / расширенная функция:

    • принимает МНОГОКРАТНЫЕ входные объекты через входные данные конвейера и / или массивные параметры И
    • ошибки возникают для СПЕЦИАЛЬНЫХ объектов ввода , И
    • эти ошибки НЕ ПРЕДОТВРАЩАЮТ ОБРАБОТКУ ДАЛЬНЕЙШИХ ВХОДНЫХ ОБЪЕКТОВ В ПРИНЦИПЕ ( ситуативно , возможно, не осталось ни одного входного объекта и / или предыдущие входные объекты, возможно, уже были успешно обработаны) ,
      • В расширенных функциях используйте $PSCmdlet.WriteError(), чтобы сообщить о непрекращающейся ошибке (Write-Error, к сожалению, не приводит к установке $? в $False в области действия вызывающего абонента - см. этот выпуск GitHub ).
      • Обработка не прекращающаяся ошибка: $? сообщает вам, указала ли самая последняя команда хотя бы одну не прекращающуюся ошибку.
        • Таким образом, $?, будучи $False, может означать, что любое (непустое) подмножество входных объектов не было обработано должным образом, возможно, весь набор.
        • Переменная предпочтения $ErrorActionPreference и / или параметр общего командлета -ErrorAction могут изменять поведение не прекращающихся ошибок (только) с точки зрения поведения вывода ошибок и того, следует ли переводить не прекращающиеся ошибки в script -конечные.
  • Сообщить ЗАКЛЮЧИТЕЛЬНОЕ ЗАЯВЛЕНИЕ Ошибка в во всех других случаях .

    • В частности, , если возникает ошибка в командлете / расширенной функции, которая принимает только один входной или нулевой входной объект и выводит NO или единственный выходной объект.
      • В расширенных функциях вы должны использовать $PSCmdlet.ThrowTerminatingError() для генерации ошибки завершения оператора.
      • Обратите внимание, что, напротив, ключевое слово Throw генерирует ошибку, определяющую сценарий , которая прерывает весь сценарий (технически: текущий поток ) .
      • Обработка ошибка завершения оператора: можно использовать обработчик try/catch или оператор trap (который нельзя использовать с без завершения ошибки), но обратите внимание, что даже ошибки, определяющие оператор по умолчанию, не препятствуют выполнению остальной части сценария. Как и в случае не прекращающихся ошибок , $? отражает $False, если предыдущий оператор вызвал ошибку завершения оператора.

К сожалению, не все собственные командлеты ядра PowerShell воспроизводятся по этим правилам :

  • Хотя это маловероятно, New-TemporaryFile (PSv5 +) сообщит о нескончаемой ошибке, если она не удастся, несмотря на то, что она не принимает входные данные конвейера и создает только один выходной объект - это, вероятно, изменится в v6 однако: см. этот выпуск GitHub .

  • Resume-Job утверждает, что передача неподдерживаемого типа задания (например, задания, созданного с помощью Start-Job, что не поддерживается, поскольку Resume-Job применяется только к workflow заданий ) вызывает ошибку завершения, но это не относится к PSv5.1.

7 голосов
/ 15 февраля 2012

Write-Error позволяет потребителю функции подавить сообщение об ошибке с помощью -ErrorAction SilentlyContinue (или -ea 0). В то время как throw требует try{...} catch {..}

Чтобы попробовать ... поймать с Write-Error:

try {
    SomeFunction -ErrorAction Stop
}
catch {
    DoSomething
}
5 голосов
/ 07 января 2017

Дополнение к Ответ Энди Арисменди :

Завершение или остановка Ошибка записи в зависимости от настройки $ErrorActionPreference.

Для нетривиальных сценариев $ErrorActionPreference = "Stop" является рекомендуемой рекомендуемой настройкой для быстрого сбоя.

"Поведение PowerShell по умолчанию в отношении ошибок, то естьпродолжить при ошибке ... чувствует себя очень VB6 "при ошибке возобновить далее" -ish "

(из http://codebetter.com/jameskovacs/2010/02/25/the-exec-problem/)

Однако, Write-Error завершает вызовы.

Чтобы использовать Write-Error в качестве не завершающей команды независимо от других настроек среды, вы можете использовать общий параметр -ErrorAction со значением Continue:

 Write-Error "Error Message" -ErrorAction:Continue
0 голосов
/ 10 марта 2014

Если вы правильно прочитали код, значит, вы правы.Для устранения ошибок следует использовать throw, и если вы имеете дело с типами .NET, полезно также следовать соглашениям об исключениях .NET.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...