Когда мне следует использовать Write-Error или Throw? Завершающие и непрекращающиеся ошибки

Посмотрев на сценарий 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 с последующим Return. Есть разница?


person Bill Barry    schedule 15.02.2012    source источник
comment
Что ты имеешь в виду? Если Write_error позволяет сценарию продолжить, вполне понятно, что после Write-Error будет иметься оператор возврата. Ошибка была записана, и вы вернетесь к коду, который изначально вызвал функцию. Поскольку Throw предназначен для устранения ошибок, он автоматически завершается, поэтому оператор return в объявлении throw бесполезен.   -  person Gisli    schedule 15.02.2012
comment
@Gisli: Важно отметить, что return не возвращается к вызывающему в блоке process (расширенной) функции; вместо этого он переходит к следующему входному объекту в конвейере. Действительно, это типичный сценарий генерации непрекращающихся ошибок: если обработка дальнейших входных объектов все еще возможна.   -  person mklement0    schedule 27.09.2016
comment
Обратите внимание, что Throw генерирует ошибку, завершающую сценарий, что не то же самое, что и ошибки, завершающие оператор, вызванные, например, Get-Item -NoSuchParameter или 1 / 0.   -  person mklement0    schedule 16.08.2017


Ответы (6)


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. Это не нарушит обработку, как можно было бы ожидать.

Больше информации:

person Andy Arismendi    schedule 15.02.2012
comment
Итак, Throw остановит все, Write-Error + return остановит только текущую функцию. - person Bill Barry; 15.02.2012
comment
@BillBarry Я немного обновил свой ответ, объяснив return. - person Andy Arismendi; 15.02.2012
comment
Как насчет Write-Error, за которым следует exit (1), чтобы обеспечить возврат в ОС соответствующего кода ошибки? Это когда-нибудь уместно? - person pabrams; 15.06.2018

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

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

  • скрипт - устранение ошибок (точнее: runspace - завершение), вызванных либо Throw, либо эскалация одного из других типов ошибок с помощью общий -ErrorAction параметр, -ErrorAction Stop или через _ 4_ предпочтительная переменная, $ErrorActionPreference = 'Stop'.
    Если они не обнаружены, они завершают текущую область выполнения (поток); то есть они завершают не только текущий сценарий, но и всех его вызывающих, если применимо).

Полный обзор обработки ошибок PowerShell см. В выпуске документации GitHub № 1583.

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


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

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

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

  • Сообщите об ошибке НЕПРЕРЫВНОЙ, если ваш командлет / расширенная функция:

    • accepts MULTIPLE input objects, via the pipeline input and/or array-valued parameters, AND
    • ошибки возникают для КОНКРЕТНЫХ объектов ввода И
    • these errors DO NOT PREVENT PROCESSING of FURTHER input objects IN PRINCIPLE (situationally, there may be no input objects left and/or previous input objects may already have been processed successfully).
      • In advanced functions, use $PSCmdlet.WriteError() to report a non-terminating error (Write-Error, unfortunately, doesn't cause $? to be set to $False in the caller's scope - see GitHub issue #3629).
      • Handling a non-terminating error: $? tells you whether the most recent command reported at least one non-terminating error.
        • Thus, $? being $False can either mean that any (nonempty) subset of input objects weren't properly processed, possibly the entire set.
        • Переменная предпочтения $ErrorActionPreference и / или общий параметр командлета -ErrorAction могут изменять поведение непрекращающихся ошибок (только) с точки зрения поведения вывода ошибок и того, должны ли непрекращающиеся ошибки перерасти в ошибки, завершающие скрипт.
  • Сообщайте об ошибке STATEMENT-TERMINATING во всех остальных случаях.

    • Notably, if an error occurs in a cmdlet / advanced function that only accepts a SINGLE or NO input object and outputs NO or a SINGLE output object or takes parameter input only and the parameter values given prevent meaningful operation.
      • In advanced functions, you must use $PSCmdlet.ThrowTerminatingError() in order to generate a statement-terminating error.
      • Обратите внимание, что, напротив, ключевое слово Throw генерирует ошибку, завершающую скрипт, которая прерывает весь скрипт (технически: текущий поток).
      • Обработка ошибки завершения оператора: можно использовать try/catch обработчик или trap оператор (который нельзя использовать с непрекращающимися ошибками), но обратите внимание, что даже ошибки, устраняющие оператор по умолчанию, не препятствуют выполнению остальной части скрипта. Как и в случае с непрекращающимися ошибками, $? отражает $False, если предыдущий оператор вызвал ошибку завершения оператора.

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

  • Хотя вероятность сбоя маловероятна, New-TemporaryFile (PSv5 +) сообщит о неустранимой ошибке в случае сбоя, несмотря на то, что не принимает ввод конвейера и создает только один объект вывода - это было исправлено по крайней мере в PowerShell [Core] 7.0, однако: см. Проблема GitHub № 4634.

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

person mklement0    schedule 09.10.2016

Основное различие между командлетом 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
person Enrico Campidoglio    schedule 15.02.2012
comment
если у вас $ ErrorActionPreference = Stop, Write-Error также завершит процесс. - person Michael Freidgeim; 05.01.2017
comment
Хорошая информация, но хотя поток ошибок PowerShell аналогичен текстовому потоку stderr в других оболочках, как и все потоки PowerShell, он содержит объекты, а именно [System.Management.Automation.ErrorRecord] экземпляры, которые по умолчанию собираются в автоматической $Error коллекции ($Error[0] содержит самую последнюю ошибку). Даже если вы просто используете Write-Error со строкой string, эта строка будет заключена в экземпляр [System.Management.Automation.ErrorRecord]. - person mklement0; 30.03.2017

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

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

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

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

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

Однако это приводит к завершению Write-Error вызовов.

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

 Write-Error "Error Message" -ErrorAction:Continue
person Michael Freidgeim    schedule 07.01.2017

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

Чтобы использовать try ... catch с Write-Error:

try {
    SomeFunction -ErrorAction Stop
}
catch {
    DoSomething
}
person Rynant    schedule 15.02.2012
comment
Зачем вообще нужно вызывать Write-Error, если вы хотите подавить сообщение об ошибке? - person Michael Freidgeim; 07.01.2017

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

person yzorg    schedule 10.03.2014