Совсем недавно меня спросили как можно обработать ошибку выполнения в PowerShell. Вопрос был про Errors Handling. И вопрос звучал на анлийском. Что-то я помнил, но смутно, так как плотно обработчики ошибок писать не доводилось. Поэтому пошел в сеть, нашел отличный материал по теме, и весь его протестировал. Материал богатый поэтому разбиваю его на две части. Уверен что вам тоже будет полезно.
Для начала определимся, что такое обработка ошибок вообще. В общем случае ошибка - это поведение программы или скрипта, отличное от запланированного. Совсем избежать ошибок не очень возможно, поэтому надо предвидеть, где они могут возникнуть и писать код так, чтобы при возникновении ошибки можно было перехватить ее, проанализировать и определить дальнейшее поведение скрипта. Именно это обычно и подразумевается под обработкой ошибок.
В PowerShell ошибки делятся на два типа: прерывающие (Terminating) и непрерывающие (Non-Terminating). Как следует из названия, непрерывающие ошибки позволяют продолжить выполнение команды, тогда как при возникновении прерывающей ошибки дальнейшее продолжение выполнения команды невозможно. К примеру, у нас есть файл со списком служб, которые необходимо перезапустить следующей командой:
Get-Content -Path C:\Files\services.txt | Restart-Service
Get-Content -Path C:\Files\services.txt | Restart-Service
Предположим, что перезапуск одной из перечисленных служб по какой либо причине невозможен. Тем не менее можно продолжать выполнение задачи, поскольку остальные службы доступны и их можно перезапустить. Это пример непрерывающей ошибки.
А теперь представьте, что у нас нет прав на открытие этого файла, и соответственно прочитать список служб мы не можем. В этой ситуации продолжение работы невозможно, т.е. это прерывающая ошибка.
PowerShell позволяет обрабатывать оба эти типа ошибок. Большинство ошибок в PowerShell непрерывающие, и сегодня речь пойдет о том, как их обрабатывать.
Обработка непрерывающих ошибок
Для получения ошибки возьмем службу с ″оригинальным″ названием Service. Поскольку службы этой на сервере нет, то обращение к ней стабильно будет генерировать ошибку. Запросим данные о нескольких службах командой:
Get-Service service,spooler
Get-Service service,spooler
PS D:\Courses!!!\Done!\PowerShell\scripts> Get-Service service,spooler
Get-Service : Не удается найти службу с именем службы "service".
строка:1 знак:1
+ Get-Service service,spooler + CategoryInfo : ObjectNotFound: (service:String) [Get-Service], ServiceCommandException + FullyQualifiedErrorId : NoServiceFoundForGivenName,Microsoft.PowerShell.Commands.GetServiceCommand
Status Name DisplayName
------ ---- -----------
Running spooler Диспетчер печати
Get-Service : Не удается найти службу с именем службы "service".
строка:1 знак:1
+ Get-Service service,spooler + CategoryInfo : ObjectNotFound: (service:String) [Get-Service], ServiceCommandException + FullyQualifiedErrorId : NoServiceFoundForGivenName,Microsoft.PowerShell.Commands.GetServiceCommand
Status Name DisplayName
------ ---- -----------
Running spooler Диспетчер печати
Как видно из примера, PowerShell не нашел службу Service, о чем выдал ошибку и затем продолжил выполнение команды. Давайте разберемся, почему команда повела себя именно так и как это поведение изменить.
За поведение команды при возникновении ошибки отвечает параметр ErrorAction, который может принимать одно из пяти значений:
• Continue;
• SilentlyContinue;
• Stop;
• Ignore;
• Inquire.
Примечание. Еще у ErrorAction может быть значение Suspend. Но это значение может применяться только к рабочим процессам (workflows), поэтому в рамках данной статьи речь о нем не пойдет.
Значение Continue означает, что при возникновении ошибки информация об этом будет выведена на экран (отправлена в поток вывода Error) и добавлена в автоматическую переменную $Error, после чего выполнение команды будет продолжено. Надо сказать, что Continue — это действие, определенное в сеансе по умолчанию, поэтому его можно не указывать явно.
PS D:\Courses!!!\Done!\PowerShell\scripts> Get-Service service,spooler -ErrorAction Continue
Get-Service : Не удается найти службу с именем службы "service".
строка:1 знак:1
+ Get-Service service,spooler -ErrorAction Continue + CategoryInfo : ObjectNotFound: (service:String) [Get-Service], ServiceCommandException + FullyQualifiedErrorId : NoServiceFoundForGivenName,Microsoft.PowerShell.Commands.GetServiceCommand
Status Name DisplayName
------ ---- -----------
Running spooler Диспетчер печати
При значении SilentlyContinue информация об ошибке добавляется в переменную $Error, но не выводится на экран. При этом команда продолжает выполняться дальше, также как и в предыдущем случае.
Get-Service : Не удается найти службу с именем службы "service".
строка:1 знак:1
+ Get-Service service,spooler -ErrorAction Continue + CategoryInfo : ObjectNotFound: (service:String) [Get-Service], ServiceCommandException + FullyQualifiedErrorId : NoServiceFoundForGivenName,Microsoft.PowerShell.Commands.GetServiceCommand
Status Name DisplayName
------ ---- -----------
Running spooler Диспетчер печати
При значении SilentlyContinue информация об ошибке добавляется в переменную $Error, но не выводится на экран. При этом команда продолжает выполняться дальше, также как и в предыдущем случае.
PS D:\Courses!!!\Done!\PowerShell\scripts> Get-Service service,spooler -ErrorAction SilentlyContinue
Status Name DisplayName
------ ---- -----------
Running spooler Диспетчер печати
Значение Stop останавливает дальнейшее выполнение команды при возникновении ошибки. И наоборот, значение Ignore полностью игнорирует возникновение ошибки, при этом не выводится сообщение на экран и не производится запись в $Error. Это значение появилось в PowerShell 3.0.
Status Name DisplayName
------ ---- -----------
Running spooler Диспетчер печати
Значение Stop останавливает дальнейшее выполнение команды при возникновении ошибки. И наоборот, значение Ignore полностью игнорирует возникновение ошибки, при этом не выводится сообщение на экран и не производится запись в $Error. Это значение появилось в PowerShell 3.0.
PS D:\Courses!!!\Done!\PowerShell\scripts> Get-Service service,spooler -ErrorAction Stop
Get-Service : Не удается найти службу с именем службы "service".
строка:1 знак:1
+ Get-Service service,spooler -ErrorAction Stop + CategoryInfo : ObjectNotFound: (service:String) [Get-Service], ServiceCommandException + FullyQualifiedErrorId : NoServiceFoundForGivenName,Microsoft.PowerShell.Commands.GetServiceCommand
Inquire — наиболее интересное значение ErrorAction. Если задать это значение, то при возникновении ошибки предлагается на выбор несколько действий: продолжить (Yes), продолжить не смотря на эту и все последующие ошибки (Yes to All), остановить (Halt) или приостановить (Suspend) выполнение команды.
Самый необычный эффект дает Suspend, при выборе которого открывается параллельный сеанс (Nested Namespace). Определить его можно по значку >>. Nested Namespace представляет из себя дочерний процесс, в котором можно полноценно работать — выполнять команды, запускать скрипты и т.п. Этот режим удобно использовать для отладки скриптов, например можно по быстрому исправить причину ошибки и продолжить выполнение. Для выхода из Nested Namespace достаточно набрать exit и выбрать необходимое действие.
PS D:\Courses!!!\Done!\PowerShell\scripts> Get-Service service,spooler -ErrorAction Inquire
Подтверждение
Не удается найти службу с именем службы "service".
[Y] Да - Y [A] Да для всех - A [H] Прервать команду - H [S] Приостановить - S [?] Справка
(значением по умолчанию является "Y"):s
PS D:\Courses!!!\Done!\PowerShell\scripts>> Get-Service service,spooler -ErrorAction Inquire
Подтверждение
Не удается найти службу с именем службы "service".
[Y] Да - Y [A] Да для всех - A [H] Прервать команду - H [S] Приостановить - S [?] Справка
(значением по умолчанию является "Y"):y
Get-Service : Не удается найти службу с именем службы "service".
строка:1 знак:1
+ Get-Service service,spooler -ErrorAction Inquire + CategoryInfo : ObjectNotFound: (service:String) [Get-Service], ServiceCommandException + FullyQualifiedErrorId : NoServiceFoundForGivenName,Microsoft.PowerShell.Commands.GetServiceCommand
Status Name DisplayName
------ ---- -----------
Running spooler Диспетчер печати
Примечание. У параметра ErrorAction есть алиас — EA. Кроме того, вместо названия параметра можно указывать числовые значения: 0 (SilentlyContinue), 1 (Stop), 2 (Continue), 3 (Inquire). Так например, вместо:
Get-Service service,spooler -ErrorAction SilentlyContinue
можно написать так:
Get-Service service,spooler -EA 0
Подтверждение
Не удается найти службу с именем службы "service".
[Y] Да - Y [A] Да для всех - A [H] Прервать команду - H [S] Приостановить - S [?] Справка
(значением по умолчанию является "Y"):s
PS D:\Courses!!!\Done!\PowerShell\scripts>> Get-Service service,spooler -ErrorAction Inquire
Подтверждение
Не удается найти службу с именем службы "service".
[Y] Да - Y [A] Да для всех - A [H] Прервать команду - H [S] Приостановить - S [?] Справка
(значением по умолчанию является "Y"):y
Get-Service : Не удается найти службу с именем службы "service".
строка:1 знак:1
+ Get-Service service,spooler -ErrorAction Inquire + CategoryInfo : ObjectNotFound: (service:String) [Get-Service], ServiceCommandException + FullyQualifiedErrorId : NoServiceFoundForGivenName,Microsoft.PowerShell.Commands.GetServiceCommand
Status Name DisplayName
------ ---- -----------
Running spooler Диспетчер печати
Примечание. У параметра ErrorAction есть алиас — EA. Кроме того, вместо названия параметра можно указывать числовые значения: 0 (SilentlyContinue), 1 (Stop), 2 (Continue), 3 (Inquire). Так например, вместо:
Get-Service service,spooler -ErrorAction SilentlyContinue
можно написать так:
Get-Service service,spooler -EA 0
Переменные для обработки ошибок
Как я уже говорил, если не указывать параметр ErrorAction, то для команды действует режим обработки ошибок, определенный в сеансе. Этот режим задается переменной $ErrorActionPreference, которая по умолчанию имеет значение Continue. При желании можно переопределить режим для всего сеанса, задав переменной $ErrorActionPreference нужное значение.
PS D:\Courses!!!\Done!\PowerShell\scripts>> $ErrorActionPreference
Continue
PS D:\Courses!!!\Done!\PowerShell\scripts>> $ErrorActionPreference
Continue
Все ошибки PowerShell сохраняет в автоматическую переменную $Error. Это глобальная переменная, которая представляет из себя массив строк, содержащий записи обо всех ошибках в текущем сеансе. Каждая новая ошибка добавляется в начало массива, соответственно для просмотра последней ошибки надо обратиться к самому первому элементу массива $Error[0].
$Error имеет свои свойства и методы, которые можно использовать. Например, посмотреть общее количество ошибок в текущем сеансе можно командой $Error.Count, а очистить список — командой $Error.Clear().
PS D:\Courses!!!\Done!\PowerShell\scripts>> $Error[0]
Не удается преобразовать значение "SilentContinue" в тип "System.Management.Automation.ActionPreference". Ошибка: "Не удается сопоставить пустое имя идентификатора SilentContinue с допустимым именем перечислителя. Укажите одно из следующих имен перечислителя и попробуйте еще раз: SilentlyContinue, Stop, Continue, Inquire, Ignore"
строка:1 знак:1
+ $ErrorActionPreference = "SilentContinue"
$Error имеет свои свойства и методы, которые можно использовать. Например, посмотреть общее количество ошибок в текущем сеансе можно командой $Error.Count, а очистить список — командой $Error.Clear().
PS D:\Courses!!!\Done!\PowerShell\scripts>> $Error[0]
Не удается преобразовать значение "SilentContinue" в тип "System.Management.Automation.ActionPreference". Ошибка: "Не удается сопоставить пустое имя идентификатора SilentContinue с допустимым именем перечислителя. Укажите одно из следующих имен перечислителя и попробуйте еще раз: SilentlyContinue, Stop, Continue, Inquire, Ignore"
строка:1 знак:1
+ $ErrorActionPreference = "SilentContinue"
+ CategoryInfo : MetadataError: (:) [], ArgumentTransformationMetadataException
+ FullyQualifiedErrorId : RuntimeException
PS D:\Courses!!!\Done!\PowerShell\scripts>> $Error.Count
8
PS D:\Courses!!!\Done!\PowerShell\scripts>> $Error.Clear()
PS D:\Courses!!!\Done!\PowerShell\scripts>> $Error.Count
0
+ FullyQualifiedErrorId : RuntimeException
PS D:\Courses!!!\Done!\PowerShell\scripts>> $Error.Count
8
PS D:\Courses!!!\Done!\PowerShell\scripts>> $Error.Clear()
PS D:\Courses!!!\Done!\PowerShell\scripts>> $Error.Count
0
Переменная $Error не безразмерна, по умолчанию она хранит не более 256 ошибок. При превышении этого количества наиболее старые ошибки будут затираться. При необходимости количество записей в переменной $Error можно увеличить, изменив значение другой переменной $MaximumErrorCount.
Кроме $Error для хранения ошибок допускается задавать собственные переменные. Сделать это можно с помощью параметра ErrorVariable, например так:
Get-Service service,spooler -ErrorAction SilentlyContinue -ErrorVariable var
Get-Service service,spooler -ErrorAction SilentlyContinue -ErrorVariable var
Обратите внимание, что имя переменной в команде задается без знака $, хотя в дальнейшем к ней обращаемся как к обычной переменной $var.
PS D:\Courses!!!\Done!\PowerShell\scripts>> Get-Service service,spooler -ErrorAction SilentlyContinue -ErrorVariable var
Status Name DisplayName
------ ---- -----------
Running spooler Диспетчер печати
PS D:\Courses!!!\Done!\PowerShell\scripts>> $var
Get-Service : Не удается найти службу с именем службы "service".
строка:1 знак:1
+ Get-Service service,spooler -ErrorAction SilentlyContinue -ErrorVariable var
CategoryInfo : ObjectNotFound: (service:String) [Get-Service], ServiceCommandException
+ FullyQualifiedErrorId : NoServiceFoundForGivenName,Microsoft.PowerShell.Commands.GetServiceCommand
PS D:\Courses!!!\Done!\PowerShell\scripts>> Get-Service service,spooler -ErrorAction SilentlyContinue -ErrorVariable var
Status Name DisplayName
------ ---- -----------
Running spooler Диспетчер печати
PS D:\Courses!!!\Done!\PowerShell\scripts>> $var
Get-Service : Не удается найти службу с именем службы "service".
строка:1 знак:1
+ Get-Service service,spooler -ErrorAction SilentlyContinue -ErrorVariable var
CategoryInfo : ObjectNotFound: (service:String) [Get-Service], ServiceCommandException
+ FullyQualifiedErrorId : NoServiceFoundForGivenName,Microsoft.PowerShell.Commands.GetServiceCommand
В отличие от глобальной переменной $Error заданные вручную переменные хранят только ошибки той команды, в которой они определены. Кроме того, по умолчанию эти переменные каждый раз перезаписываются и поэтому хранят только последнюю ошибку. Если вы хотите, чтобы новые ошибки добавлялись в переменную, не перезаписывая ее содержимое, то перед именем переменной надо поставить знак +, например +var.
В следующей части речь пойдет про обработку прерывающих ошибок (exceptions).
No comments:
Post a Comment
А что вы думаете по этому поводу?