Коли я повинен використовувати Write-Error vs. Throw? Помилки, що припиняються проти не закінчуються


145

Дивлячись на сценарій 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 з подальшим поверненням. Чи є різниця?


4
Що ви маєте на увазі? Якщо Write_error дозволяє сценарію продовжуватись, то зрозумілим є повідомлення про повернення після Write-Error. Помилка виписана, і ви повернетесь до коду, який викликав функцію в першу чергу. Оскільки Throw призначений для припинення помилок, він автоматично припиняється, так що виписка про повернення в декларації про кидання є марною
Gisli

1
@Gisli: Важливо відзначити , що returnце НЕ повернення до викликає в processблоці (розширений) функції; натомість він переходить до наступного вхідного об'єкта в конвеєрі. Дійсно, це типовий сценарій для генерування непомилкових помилок: якщо обробка подальших вхідних об'єктів все ще можлива.
mklement0

1
Зверніть увагу , що Throwгенерує сценарій помилки -terminating, яка не збігається з твердженням -terminating помилки , що запускаються, приміром, з допомогою Get-Item -NoSuchParameterабо 1 / 0.
mklement0

Відповіді:


188

Write-Errorслід використовувати, якщо ви хочете повідомити користувача про некритичну помилку. За замовчуванням це лише друк повідомлення про помилку червоним текстом на консолі. Це не перешкоджає продовженню трубопроводу чи петлі. Throwз іншого боку, створює те, що називається помилкою закінчення. Якщо ви використовуєте кидок, конвеєр та / або поточний контур буде припинено. Насправді все виконання буде припинено, якщо ви не використовуєте a 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але це означатиме, що вам доведеться використовувати a 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. Він не порушить обробку, як можна було б очікувати.

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


Гаразд, так Throw зупинить усе, Write-Error + return зупинить лише поточну функцію.
Білл Баррі

@BillBarry Я трохи оновив свою відповідь із поясненням return.
Енді Арісменді

Що з записом-помилка з подальшим виходом (1), щоб забезпечити повернення відповідного коду помилки в ОС? Це колись доречно?
пабрам

19

Основна відмінність командлета 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
якщо у вас $ ErrorActionPreference = "Зупинити", запис-помилка також припинить процес.
Майкл Фрейджім

4
Гарна інформація, але в той час як потік помилок PowerShell є аналогом для тексту на основі Stderr потоку в інших оболонках, як потоки всіх PowerShell містять об'єкти , а саме [System.Management.Automation.ErrorRecord]екземпляри, які за умовчанням , зібране в автоматичному $Errorзборі ( $Error[0]містить найостаннішу помилку). Навіть якщо ви просто використовувати Write-Errorз рядком , цей рядок отримує загорнуті в [System.Management.Automation.ErrorRecord]екземплярі.
mklement0

16

Важливо : Є два типи помилок завершення , які поточні теми довіри, на жаль, суперечать :

  • Заява -terminating помилка, як повідомляють командлети в деяких невідновлювальних ситуаціях і виразамив яких / відбувається помилка PS виконання виключення .NET; тільки оператор припиняється, а виконання сценарію триває за замовчуванням .

  • Сценарій -terminating помилок (точніше: простір виконання кінцевого ), або як запускаютьсяThrowабо шляхом ескалації один з інших типів помилокдопомогою помилок дій значення переваги змінного / параметраStop.
    Якщо їх не спіймали, вони припиняють поточне пробіг (нитку); тобто вони припиняють не тільки поточний скрипт, але і всі його абоненти, якщо це застосовується).

Для вичерпного огляду помилок PowerShell дивіться цю проблему з документацією GitHub .

Інша частина цієї публікації присвячена помилкам, що не закінчуються та констатують оператори .


Для того, щоб доповнити існуючі корисні відповіді з акцентом на суть питання: Як ви вибираєте чи , щоб повідомити про Оператор- термінатор або НЕ кінцеве помилку ?

Повідомлення про помилки Cmdlet містить корисні вказівки; дозвольте спробувати прагматичне резюме :

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

  • Повідомте про помилку, що не припиняється , якщо ваша командна команда / розширена функція:

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

    • Зокрема, якщо в командлеті / розширеній функції виникає помилка, яка приймає лише вхідний об'єкт SINGLE або NO і виводить NO або SINGLE вихідний об'єкт або приймає лише введення параметрів, а вказані значення параметрів перешкоджають значній роботі.
      • У розширених функціях ви повинні використовувати $PSCmdlet.ThrowTerminatingError() для створення помилки, що закінчується оператором.
      • Слід зазначити , що, на відміну від цього Throwключового слова генерує сценарій помилки -terminating , що перериває весь скрипт (технічно: поточна нитка ).
      • Поводження з помилкою закінчення оператора: може бути використаний try/catchобробник чи trapоператор (який не можна використовувати з помилками, що не припиняються ), але зауважте, що навіть помилки, що підтверджують операцію, за замовчуванням не заважають виконувати решту сценарію. Як і у випадку помилок, що не закінчуються , $?відображається, $Falseякщо попереднє твердження викликало помилку, що закінчує оператор

На жаль, не всі власні командлети PowerShell грають за цими правилами :

  • Хоча навряд чи New-TemporaryFileвийде з ладу, (PSv5 +) повідомив би про нестабільну помилку, якщо вона не вдалася, незважаючи на те, що не приймає вхід в конвеєр та створює лише один вихідний об'єкт - це було виправлено як мінімум з PowerShell [Core] 7.0, однак: див. Цей GitHub випуск .

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


8

Write-Errorдозволяє споживачеві функції придушити повідомлення про помилку за допомогою -ErrorAction SilentlyContinue(альтернативно -ea 0). Хоча throwвимагаєtry{...} catch {..}

Щоб використати спробу ... ловлі за допомогою Write-Error:

try {
    SomeFunction -ErrorAction Stop
}
catch {
    DoSomething
}

Чому взагалі потрібно дзвонити Write-Error, якщо ви хочете придушити повідомлення про помилку?
Майкл Фрейджім

8

Доповнення до відповіді Енді Арісменді :

Завершить процес запису-помилки процес чи ні, залежить від $ErrorActionPreferenceналаштування.

Для нетривіальних скриптів, $ErrorActionPreference = "Stop"це рекомендоване значення потерпіти невдачу швидко.

"Поведінка PowerShell за замовчуванням щодо помилок, які слід продовжувати помилки ... дуже VB6" On Error Resume Next "-ish"

(від http://codebetter.com/jameskovacs/2010/02/25/the-exec-problem/ )

Однак Write-Errorдзвінки припиняються.

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

 Write-Error "Error Message" -ErrorAction:Continue

0

Якщо ваше читання коду є правильним, то ви правильні. Помилкові помилки повинні використовуватись throw, і якщо ви маєте справу з типами .NET, то також корисно дотримуватись конвенцій про виключення .NET.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.