Когда я должен использовать Write-Error vs 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 для непрекращающихся ошибок и бросать для прерывания ошибок, поэтому мне кажется, что вы не должны использовать Write-Error с последующим возвратом. Есть ли разница?

6 112

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

Подробнее:

основное различие между 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 или путем эскалации одного из других типов ошибок с помощью параметра error-action preference-variable / parameter value 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
}

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

ли Write-Error завершает процесс или нет, зависит от $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

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