Foreversoft.ru

IT Справочник
0 просмотров
Рейтинг статьи
1 звезда2 звезды3 звезды4 звезды5 звезд
Загрузка...

Powershell обработка ошибок

Перехват ошибок с помощью –ErrorAction (–EA) в Windows PowerShell

Посетителей: 5737 | Просмотров: 8411 (сегодня 0)Шрифт:

Следует признаться, что у моей функции была и слабая сторона – неспособность корректно разбираться с ошибками, которые могут возникнуть, такими как проблемы подключения или разрешений. Именно к ней я и хочу обратиться в выпуске рубрики Windows PowerShell за этот месяц – я рассматриваю возможности обработки ошибок, предлагаемые Windows PowerShell.

Установка ловушки

Ключевое слово Trap («Ловушка») в Windows PowerShell определяет обработчик ошибок. Когда в сценарии происходит исключение, оболочка смотрит, была ли определена ловушка – это значит, что ловушка должна находиться в сценарии до появления исключений. Для этой демонстрации я собрал тестовый сценарий, который, как я знаю, создаст проблему с подключением. Я использую Get-WmiObject для подключения к имени компьютера, которое, как я знаю, не существует в сети. Моя задача заключается в том, чтобы ловушка для ошибок записала неверное имя компьютера в файл, дав мне файл с именами компьютеров, которые не сработали. Я также включу подключения к двум компьютерам, которые находятся в зоне доступа. Сценарий показан на рис. 1.

Рис. 1. Добавление ловушки

Данные, выдаваемые этим сценарием и показанные на рис. 2, – не совсем то, что мне нужно. Обратите внимание на то, что сообщение «Error connecting to…» («Ошибка при подключении к…») не отображается. Файл Errors.txt также не был создан. Другими словами, моя ловушка не сделала ничего. Что же случилось?

Рис. 2. Это не те выходные данные, на которые я надеялся!

Ключевым моментом здесь является понимание того, что обычное сообщение оболочки об ошибке – это не то же самое, что исключение. (Существуют непрерывающие и прерывающие ошибки. Прерывающие ошибки останавливают исполнение конвейера и приводят к исключению.) Улавливать можно только исключения. Когда происходит ошибка, оболочка сверяется со своей встроенной переменной $ErrorActionPreference, чтобы узнать, что следует делать. По умолчанию переменная имеет значение «Continue», что означает «отобразить сообщение об ошибке и продолжить работать». Изменение этой переменной на «Stop» заставит ее отобразить сообщение об ошибке и произвести исключение, которое можно уловить. Однако это значит, что любая ошибка в сценарии будет давать такой результат.

Лучшей методикой является использования поведения «Stop» лишь тем командлетом, от которого ожидаются проблемы. Это можно сделать, используя параметр –ErrorAction (или –EA), общий параметр, поддерживаемый всеми командлетами. Скорректированная версия сценария показана на рис. 3. Она работает в точности как и ожидается, производя результаты, которые можно найти на рис. 4.

Рис. 3. Использование -ErrorAction

Рис. 4. Я получаю более полезные результаты при использовании параметра –ErrorAction

Использование Continue в конце ловушки дает указание оболочке возобновить исполнение на строке кода, после строки, создавшей исключение. Другим вариантом является использование ключевого слова Break («Прервать») (я расскажу о нем чуть ниже). Также обратите внимание на то, что переменная $computer, определенная в сценарии, остается действительной внутри ловушки. Это обусловлено тем, что ловушка является дочерней областью самого сценария, а это значит, что ловушка может видеть все переменные внутри сценария (дополнительные сведения об этом также будут чуть ниже).

Все дело в области

Особенно коварным аспектом улавливания ошибок в Windows PowerShell является использование области. Сама оболочка представляет собой глобальную область, содержащую все, что происходит внутри оболочки. Сценарий при выполнении получает собственную область. В случае определения функции, внутренность функции является ее собственной, частной областью и так далее. Это создает своего рода родительскую/дочернюю иерархию.

Когда происходит исключение, оболочка ищет ловушку в текущей области. Это значит, что исключение внутри функции будет искать ловушку внутри этой функции. Если оболочка находит ловушку, она исполняет ловушку. Если в конце ловушки стоит Continue, оболочка возобновит исполнение строки кода, следующей за той, что вызвала исключение – оставаясь в той же области. Вот немного псевдокода, чтобы помочь проиллюстрировать это:

В случае ошибки в строке 5 исполнится ловушка в строке 1. Ловушку завершает Continue, так что исполнение продолжится на строке 6.

Теперь рассмотрим этот немного другой экземпляр области:

Если ошибка произойдет на строке 7, оболочка будет искать ловушку внутри области функции. Таковой нет, так что оболочка выходит из области функции и ищет ловушку в родительской области. Ловушка там есть, так что она исполняется в строке 1. В данном случае Continue возобновится на строке кода в той же области, что следовала за исключением – это строка 12, а не строка 8. Другими словами, оболочка не входит обратно в функцию после выхода из нее.

Теперь сравните это поведение с данным примером:

В данном случае ошибка на строке 6 исполнит ловушку на строке 2, оставаясь внутри области функции. Ключевое слово Continue останется внутри области, возобновляя исполнение на строке 7. Это достоинство включения ловушки в область, где ожидается ошибка, – невыход из области и способность возобновить выполнение внутри нее. Но что если данный метод не работает в конкретном случае?

Читать еще:  Код ошибки 812

Командлет месяца: Compare-Object

Это замечательное средство для управления нормативами настройки. Compare-Object или его псевдоним Diff, разработан для сравнения друг с другом двух наборов объектов. По умолчанию, он сравнивает каждое свойство каждого объекта и все различия выдаются командой. Представим себе, что службы сервера настроены именно так, как надо. Достаточно выполнить это, чтобы создать норматив:

Почти каждый объект может быть передан Export-CliXML, который превратить объекты в файл XML. Позднее можно выполнить ту же команду (такую как Get-Service) и сравнить результаты с сохраненным XML. Вот так:

Добавление параметра –property заставляет сравнение рассматривать лишь это свойство, а не весь объект. В этом случае будет получен список имен служб, отличающихся от норматива, дающий знать, были ли какие-то службы добавлены или удалены после его создания.

Вырываясь из ловушки

Ранее я упомянул ключевое слово Break. Рис. 5 показывает пример того, как ключевое слово Break можно применить в деле.

Рис. 5. Использование ключевого слова Break

Быстрый обзор пути цепи исполнения таков. Строка 19 исполняется первой, вызывая функцию на строке 6. Строка 15 исполняется и создает исключение. Это исключение оказывается уловленным на строке 7 и затем, на строке 9, ловушка должна принять решение. Предполагая, что $condition имеет значение True, ловушка возобновит исполнение на строке 16.

Но если $condition имеет значение False, ловушка использует Break. Это означает выход из текущей области и передачу первоначального исключения выше, родителю. С точки зрения оболочки это значит, что строка 19 создала исключение, которое улавливается строкой 1. Ключевое слово Continue заставит оболочку возобновить исполнение на строке 20.

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

Зачем беспокоиться?

В каких случаях необходимо применять улавливание ошибок? Только в тех, когда предполагается возможность ошибки и требуется какое-то иное поведение, чем простое сообщение об ошибке – например, запись ошибки в файл или отображение более полезного сообщения о ней.

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

Чтобы разобраться в улавливании ошибок, определенно нужно немного больше времени и усилий. Но по мере перехода к более сложным задачам внутри Windows PowerShell улавливание ошибок будет вполне стоить вложенных усилий, помогая создать более отполированное и профессиональное средство.

Обработка ошибок в PowerShell

Администраторы баз данных всегда стараются оградить себя от возможных проблем при помощи средств аварийного восстановления, например путем создания резервных копий. Однако, разрабатывая сценарии, мы нередко забываем принять аналогичные меры для защиты процесса от ошибок. PowerShell предусматривает средства обработки ошибок, возникающих в ходе работы сценария, что позволяет устранить неполадки и выполнить необходимую работу

Инструкция Trap для обработки ошибок

Инструкция Trap для обработки ошибок появилась еще в PowerShell 1.0, но я ее по-прежнему часто использую в сценариях. Технология PowerShell построена на базе. NET Framework, и ошибки имеют несколько уровней. Возвращаемое PowerShell сообщение об ошибке верхнего уровня редко помогает решить проблему. Для примера рассмотрим случай использования метода SMO CheckTables(‘FAST’), по сути выполняющего инструкцию DBCC CHECKDB(N’AdventureWorks’, REPAIR_FAST). При вызове этого метода без блока обработки ошибок возвращается сообщение, как на рисунке 1.

Рисунок 1. Сообщение об ошибке, возвращаемое при использовании метода SMO CheckTables (‘FAST’)

Очевидно, что такое сообщение не несет никакой полезной информации, поэтому я часто использую функцию Trap:

Это позволяет при вызове метода CheckTables(‘FAST’) получить сообщение, подобное приведенному на рисунке 2.

Рисунок 2. Сообщения об ошибках, возвращаемые функцией Trap

Это сообщение содержит гораздо больше полезной информации и позволяет точно выяснить, в чем состоит проблема, и как ее решить. Ключевое слово break после точки с запятой в инструкции trap инициирует прекращение сценария после обнаружения и обработки ошибки. Ключевое слово continue позволяет продолжить выполнение сценария после обработки ошибок без прерывания.

Метод обработки ошибок Try-Catch-Finally

В PowerShell 2.0 был впервые реализован метод обработки ошибок Try-Catch-Finally, ставший привычным для большинства. NET-разработчиков. Этот метод отличается значительно большей гибкостью в обработке возможных проблем. Дополнительным преимуществом метода является возможность указывать различные типы обработки ошибок для разных ошибок. В приведенном в листинге примере также демонстрируется запуск метода CheckTables. Однако в данном случае каждый объект ItemNotFoundException обрабатывается отдельно, после чего обрабатываются все оставшиеся ошибки способом, аналогичным показанному в инструкции trap.

Читать еще:  Ошибка приложения vpn

Очевидно, что такой вариант позволяет обрабатывать различные ошибки, а тот факт, что блоки Try-Catch-Finally могут быть вложенными, обеспечивает широкие возможности управления сценариями.

Листинг. Пример запуска метода CheckTables

SMEARG

Обработка ошибок в Powershell

В Powershell ошибки делятся на два типа:

  • Критические ошибки – при их возникновении выполнение команды прерывается.
  • Некритические ошибки – при возникновении, команда продолжает выполняться.

В зависимости от ситуации одна и та же ошибка может быть критической, а в другой — некритической.

При возникновении ошибки создаётся специальный объект, в свойствах которого содержится полная информация об ошибке.

Примером некритической ошибки может послужить удаление нескольких файлов, один из которых занят каким-то приложением и не может быть удалён. В этом случае вполне можно продолжить удаление. Но если происходит какая-то логически неделимая операция, то имеет смысл прервать её после возникновения первой ошибки. Такая ошибка будет считаться критической.

При возникновении некритической ошибки информация об ошибке заносится в переменную $error:

По умолчанию ошибки выводятся на экран, но можно перенаправить в файл с помощью оператора 2> . Например:

Переменная $error на самом деле является массивом, в котором хранится информация обо всех ошибках, возникших в текущем сеансе работы. По умолчанию длина такого массива 256 (можно посмотреть в переменной $MaximumErrorCount ). При заполнении массива полностью объекты “свежих” ошибок будут записываться в самое начало, смещая все остальные, самые старые будут удаляться. Если же нужно отловить (сохранить) конкретную ошибку, её можно перенаправить в переменную с помощью специального оператора 2>$1 :

$err = Get-ChildItem NonExistDirecory 2>&1

Для перехвата ошибки без перенаправления в стандартный выходной поток можно использовать стандартный параметр -ErrorVariable , который есть у всех командлетов:

Get-ChildItem NonExistDirecory -ErrorVariable errs

В этом случае ошибка будет перенаправлена в переменную errs . Следует обратить внимание, что в этой конструкции знак $ не нужен, но в дальнейшем при обращении к переменной нужно указывать вместе со знаком, как в обычной переменной.

Кроме того, для определения того, как завершилась предыдущая команда (ошибкой, или нет) в Powershell существует специальная переменная &? . Её значение равно $true , если предыдущая команда завершилась без ошибок и $false , если возникли ошибки.

Режимы обработки ошибок

Кроме параметра -ErrorVariable все командлеты также имеют параметр -ErrorAction , определяющий поведение Powershell при возникновении ошибки.

  • Continue — если обнаружена ошибка, то она будет отображена, а выполнение сценария будет продолжено. (Этот вариант используется по умолчанию.)
  • Inquire — если обнаружена ошибка, то сценарий будет приостановлен, а пользователь должен будет выбрать — продолжить, приостановить или прервать его выполнение.
  • SilentlyContinue — если обнаружена ошибка, то выполнение сценария будет продолжено без ее отображения (но ошибка будет занесена в $error и переменная $? будет установлена в $false ).
  • Stop — если обнаружена ошибка, то выполнение сценария будет остановлено.

Можно указать режим обработки ошибки, для каждой возможной ошибки, но если нужно установить один режим для всех ошибок нужно установить переменную $ErrorActionPreference в соответствующее значение. Например:

В этом случае при возникновении каждой ошибки будет выдан зарос, что делать дальше.

Кроме того Powershell иногда считает ошибкой то, что на самом деле ошибкой не является. Например, попытка протестировать доступность компьютера по сети, который в данный момент времени недоступен приведёт к ошибке:

Параметр -count задаёт количество отправляемых запросов (по умолчанию — 4).

Для предотвращения этого в ряде командлетов есть параметр -Quiet , который подавляет все ошибки и возвращает значение $True , если операция завершилась успешно, и значение $False , если все во время выполнения команды возникли ошибки.

Это не всё, что можно сказать про обработку ошибок в Powershell, и в будущем, я возможно продолжу эту тему и расскажу про обработку критических ошибок, про отладку скриптов и возможно что-то ещё :).

Обработка ошибок в PowerShell (часть 1)

Для начала определимся, что такое обработка ошибок вообще. В общем случае ошибка — это поведение программы или скрипта, отличное от запланированного. Совсем избежать ошибок не очень возможно, поэтому надо предвидеть, где они могут возникнуть и писать код так, чтобы при возникновении ошибки можно было перехватить ее, проанализировать и определить дальнейшее поведение скрипта. Именно это обычно и подразумевается под обработкой ошибок.

В PowerShell ошибки делятся на два типа: прерывающие (Terminating) и непрерывающие (Non-Terminating). Как следует из названия, непрерывающие ошибки позволяют продолжить выполнение команды, тогда как при возникновении прерывающей ошибки дальнейшее продолжение выполнения команды невозможно. К примеру, у нас есть файл со списком служб, которые необходимо перезапустить следующей командой:

Get-Content -Path C:Filesservices.txt | Restart-Service

Предположим, что перезапуск одной из перечисленных служб по какой либо причине невозможен. Тем не менее можно продолжать выполнение задачи, поскольку остальные службы доступны и их можно перезапустить. Это пример непрерывающей ошибки.

Читать еще:  Ошибка лицензии нет соединения с сервером

А теперь представьте, что у нас нет прав на открытие этого файла, и соответственно прочитать список служб мы не можем. В этой ситуации продолжение работы невозможно, т.е. это прерывающая ошибка.

PowerShell позволяет обрабатывать оба эти типа ошибок. Большинство ошибок в PowerShell непрерывающие, и сегодня речь пойдет о том, как их обрабатывать.

Обработка непрерывающих ошибок

Для получения ошибки возьмем службу с ″оригинальным″ названием Service. Поскольку службы этой на сервере нет, то обращение к ней стабильно будет генерировать ошибку. Запросим данные о нескольких службах командой:

Как видно из примера, PowerShell не нашел службу Service, о чем выдал ошибку и затем продолжил выполнение команды. Давайте разберемся, почему команда повела себя именно так и как это поведение изменить.

За поведение команды при возникновении ошибки отвечает параметр ErrorAction, который может принимать одно из пяти значений:

• Continue;
• SilentlyContinue;
• Stop;
• Ignore;
• Inquire.

Примечание. Еще у ErrorAction может быть значение Suspend. Но это значение может применяться только к рабочим процессам (workflows), поэтому в рамках данной статьи речь о нем не пойдет.

Значение Continue означает, что при возникновении ошибки информация об этом будет выведена на экран (отправлена в поток вывода Error) и добавлена в автоматическую переменную $Error, после чего выполнение команды будет продолжено. Надо сказать, что Continue — это действие, определенное в сеансе по умолчанию, поэтому его можно не указывать явно.

При значении SilentlyContinue информация об ошибке добавляется в переменную $Error, но не выводится на экран. При этом команда продолжает выполняться дальше, также как и в предыдущем случае.

Значение Stop останавливает дальнейшее выполнение команды при возникновении ошибки. И наоборот, значение Ignore полностью игнорирует возникновение ошибки, при этом не выводится сообщение на экран и не производится запись в $Error. Это значение появилось в PowerShell 3.0.

Inquire — наиболее интересное значение ErrorAction. Если задать это значение, то при возникновении ошибки предлагается на выбор несколько действий: продолжить (Yes), продолжить не смотря на эту и все последующие ошибки (Yes to All), остановить (Halt) или приостановить (Suspend) выполнение команды.

Самый необычный эффект дает Suspend, при выборе которого открывается параллельный сеанс (Nested Namespace). Определить его можно по значку >>. Nested Namespace представляет из себя дочерний процесс, в котором можно полноценно работать — выполнять команды, запускать скрипты и т.п. Этот режим удобно использовать для отладки скриптов, например можно по быстрому исправить причину ошибки и продолжить выполнение. Для выхода из Nested Namespace достаточно набрать exit и выбрать необходимое действие.

Примечание. У параметра 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 нужное значение.

Все ошибки PowerShell сохраняет в автоматическую переменную $Error. Это глобальная переменная, которая представляет из себя массив строк, содержащий записи обо всех ошибках в текущем сеансе. Каждая новая ошибка добавляется в начало массива, соответственно для просмотра последней ошибки надо обратиться к самому первому элементу массива $Error[0].

$Error имеет свои свойства и методы, которые можно использовать. Например, посмотреть общее количество ошибок в текущем сеансе можно командой $Error.Count, а очистить список — командой $Error.Clear().

Переменная $Error не безразмерна, по умолчанию она хранит не более 256 ошибок. При превышении этого количества наиболее старые ошибки будут затираться. При необходимости количество записей в переменной $Error можно увеличить, изменив значение другой переменной $MaximumErrorCount.

Кроме $Error для хранения ошибок допускается задавать собственные переменные. Сделать это можно с помощью параметра ErrorVariable, например так:

Get-Service service,spooler -ErrorAction SilentlyContinue -ErrorVariable var

Обратите внимание, что имя переменной в команде задается без знака $, хотя в дальнейшем к ней обращаемся как к обычной переменной $var.

В отличие от глобальной переменной $Error заданные вручную переменные хранят только ошибки той команды, в которой они определены. Кроме того, по умолчанию эти переменные каждый раз перезаписываются и поэтому хранят только последнюю ошибку. Если вы хотите, чтобы новые ошибки добавлялись в переменную, не перезаписывая ее содержимое, то перед именем переменной надо поставить знак +, например +var.

Пока все, а в следующей части пойдет о способах обработки прерывающих ошибок.

Ссылка на основную публикацию
Adblock
detector