Когда я должен использовать Write-Error vs Throw? Ошибки завершения и неуничтожения

глядя на скрипт Get-WebFile на PoshCode:http://poshcode.org/3226 Я заметил это странное для меня приспособление:--6-->

$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 ответов


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

есть одна вещь, чтобы отметить, если вы установите $ErrorActionPreference to "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

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

Подробнее:


основное различие между Write-Error и бросить ключевое слово в 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

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

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

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

для a полный обзор обработки ошибок PowerShell см. В разделе эта проблема с документацией GitHub.

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


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

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

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

  • отчет НЕПРЕРЫВАЮЩУЮ ошибка, если ваш командлет / расширенная функция:

    • принимает несколько входных объектов, через входной сигнал трубопровода и / или массив-значные параметры, и
    • ошибок для Конкретные объекты ввода и
    • эти ошибки не препятствуют дальнейшей обработке входных объектов в принципе (ситуативно, возможно, не осталось объектов ввода и / или предыдущие объекты ввода, возможно, уже были успешно обработаны).
      • в расширенных функциях используйте $PSCmdlet.WriteError() сообщить о неустранимой ошибке (Write-Error, к сожалению, не вызывает $? значение $False на абонента объем - вижу этот выпуск GitHub).
      • обращение неустранимая ошибка:$? сообщает вам, сообщила Ли самая последняя команда по крайней мере один неустранимая ошибка.
        • таким образом, $? будучи $False может означать, что любое (непустое) подмножество ввода объектов, не были должным образом обработаны, возможно, весь набор.
        • предпочтения переменной $ErrorActionPreference и/или общий параметр командлета -ErrorAction может изменять поведение неперерывных ошибок (только) с точки зрения поведения вывода ошибок и того, должны ли неперерывные ошибки быть увеличены до скрипт-прекращение близких.
  • отчет ЗАЯВЛЕНИЕ-ОКОНЧАНИЕ ошибка все остальные случаи.

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

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

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

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


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

использовать попробовать...поймать с Write-Error:

try {
    SomeFunction -ErrorAction Stop
}
catch {
    DoSomething
}

кроме ответ Энди Арисменди:

ли Пишут-Ошибка завершает процесс или не зависит от $ErrorActionPreference настройка.

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

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

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

однако, это делает Write-Error вызывает прерывание.

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

 Write-Error "Error Message" -ErrorAction:Continue

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