Овладейте блоками Try Catch в PowerShell с примерами

Вы когда-нибудь запускали сценарий или командлет PowerShell и сталкивались с оглушительной стеной текста – красного цвета – подобной показанной ниже?

Example of errors in PowerShell

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

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

В этой статье вы узнаете ошибках в PowerShell и о том, как их можно перехватывать для выполнения обработки ошибок с использованием блоков Try Catch PowerShell (а также блоков finally).

Понимание того, как работают ошибки в PowerShell

Прежде чем погружаться в обработку ошибок, давайте сначала рассмотрим несколько концепций вокруг ошибок в PowerShell. Понимание ошибок может привести к лучшим стратегиям обработки ошибок.

Автоматическая переменная $Error

В PowerShell существует много автоматических переменных, и одна из них – это автоматическая переменная $Error. PowerShell использует переменную $Error, чтобы хранить все ошибки, с которыми встречается в сеансе. Переменная $Error является массивом ошибок, отсортированным по наиболее недавним.

Когда вы впервые открываете сеанс PowerShell, переменная $Error пуста. Вы можете проверить это, вызвав переменную $Error.

The $Error variable is empty

Как видно, переменная $Error начинает с пустого значения. Но как только возникает ошибка, она добавляется и сохраняется в переменной $Error.

В приведенном ниже примере ошибка генерируется путем намеренного получения имени службы, которое не существует.

PS> Get-Service xyz
PS> $Error
PS> $Error.Count
The error is added to the $Error variable

Как видно из вывода выше, сгенерированная ошибка была добавлена в переменную $Error.

Переменная $Error содержит коллекцию ошибок, созданных в сеансе PowerShell. Каждую ошибку можно получить, вызвав ее по позиции в массиве. Последняя ошибка всегда будет находиться по индексу 0.

Например, последнюю ошибку можно получить с помощью $Error[0].

Свойства объекта $Error

Поскольку все в PowerShell является объектом, переменная $Error является объектом, а у объектов есть свойства. Перенаправив переменную $Error в cmdlet Get-Member, вы увидите список доступных свойств.

$Error | Get-Member
The $Error object properties

Чтобы определить причину ошибки, вы можете просмотреть содержимое свойства InvocationInfo с помощью следующей команды.

$Error[0].InvocationInfo
The InvocationInfo property

Теперь вы можете сделать то же самое с другими свойствами и узнать, какую дополнительную информацию можно найти!

Завершение ошибок

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

Как видно на скриншоте ниже, когда выполняется команда Get-Process notepad, команда допустима, и отображаются детали процесса notepad.

The notepad process details

Однако, когда используется параметр, который не существует, например, такой как Get-Process notepad -handle 251, командлет отображает ошибку, что параметр handle недопустим. Затем командлет завершает выполнение без отображения деталей процесса notepad.

Error is thrown because the parameter is invalid

Незавершающие ошибки

Незавершающие ошибки – это ошибки, которые не прерывают выполнение сценария или команды. Например, рассмотрим приведенный ниже код. Этот код получает список имен файлов из файла fileslist.txt. Затем сценарий проходит через каждое имя файла, считывает содержимое каждого файла и выводит его на экран.

$file_list =  Get-Content .\filelist.txt
foreach ($file in $file_list) {
    Write-Output "Reading file $file"
    Get-Content $file
}

Содержимое файла filelist.txt – это имена файлов, показанные в списке ниже.

File_1.log
File_2.log
File_3.log
File_4.log
File_5.log
File_6.log
File_7.log
File_8.log
File_9.log
File_10.log

Но что если File_6.log на самом деле не существует? Когда вы запускаете код, вы ожидаете, что произойдет ошибка, потому что сценарий не может найти File_6.log. Ниже показано аналогичное выводимое сообщение.

Example of non-terminating error

Как видно на снимке экрана с результатом выше, сценарий смог прочитать первые пять файлов в списке, но при попытке прочитать файл File_6.txt, возникает ошибка. Затем сценарий продолжил чтение оставшихся файлов перед завершением. Он не прервался.

Переменная $ErrorActionPreference

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

В PowerShell есть концепция, называемая pпеременные ссылки. Эти переменные используются для изменения поведения PowerShell во многих различных ситуациях. Одной из этих переменных является $ErrorActionPreference.

Переменная $ErrorActionPreference используется для изменения способа обработки непрерывающихся ошибок в PowerShell. По умолчанию значение $ErrorActionPreference установлено на Continue. Изменение значения переменной $ErrorActionPreference на STOP заставляет PowerShell рассматривать все ошибки как прерывающиеся.

Используйте приведенный ниже код для изменения значения $ErrorActionPreference.

$ErrorActionPreference = "STOP"

Чтобы узнать больше о других допустимых значениях переменной $ErrorActionPreference, посетите PowerShell ErrorActionPreference.

Теперь обратитесь к примеру, использованному в разделе Non-Terminating Errors в данной статье. Сценарий можно изменить, чтобы включить изменение в $ErrorActionPreference, как показано в коде ниже:

# Установите значение $ErrorActionPreference в STOP
$ErrorActionPreference = "STOP"
$file_list =  Get-Content .\filelist.txt
foreach ($file in $file_list) {
    Write-Output "Reading file $file"
    Get-Content $file
}

Запуск измененного кода выше будет вести себя иначе, чем раньше, когда значение $ErrorActionPreference установлено в значение по умолчанию Continue.

Forcing a terminating error using the $ErrorActionPreference variable

Как видно на скриншоте результата выше, сценарий смог прочитать первые пять файлов в списке, но когда он попытался прочитать файл File_6.txt, возникла ошибка, потому что файл не был найден. Затем сценарий завершился, и остальные файлы не были прочитаны.

Значение $ErrorActionPreference действительно только в текущем сеансе PowerShell. Оно сбрасывается в значение по умолчанию при запуске нового сеанса PowerShell.

Общий параметр ErrorAction

Если значение $ErrorActionPreference применено к сеансу PowerShell, параметр ErrorAction применяется к любой командлету, поддерживающему общие параметры. Параметр ErrorAction принимает те же значения, что и переменная $ErrorActionPreference.

Значение параметра ErrorAction имеет приоритет над значением переменной $ErrorActionPreference.

Вернемся и используем тот же код, что и в предыдущем примере. Но на этот раз параметр ErrorAction добавлен к строке Get-Content.

# Установите значение $ErrorActionPreference по умолчанию (CONTINUE)
$ErrorActionPreference = "CONTINUE"
$file_list =  Get-Content .\filelist.txt
foreach ($file in $file_list) {
    Write-Output "Reading file $file"
		# Используйте общий параметр -ErrorAction
		Get-Content $file -ErrorAction STOP
}

После выполнения измененного кода вы увидите, что, несмотря на то, что $ErrorActionPreference установлен в Continue, сценарий все равно завершился после возникновения ошибки. Сценарий завершился, потому что значение параметра PowerShell ErrorAction в Get-Content установлено в STOP.

Forcing a terminating error using the PowerShell ErrorAction parameter

Использование блоков PowerShell Try Catch

На этом этапе вы узнали о ошибках PowerShell и о том, как работают переменная $ErrorActionPreference и параметры ErrorAction PowerShell. Теперь пришло время узнать о полезной штуке – блоках PowerShell Try Catch Finally.

Блоки PowerShell try catch (и необязательный finally block) представляют собой способ обернуть кусок кода в сеть и перехватить любые ошибки, которые возвращаются.

Ниже приведен синтаксис оператора Try.

try {
    <statement list>
}
catch [[<error type>][',' <error type>]*]{
    <statement list>
}
finally {
    <statement list>
}

Блок Try содержит код, который вы хотите, чтобы PowerShell “попытался” выполнить и отследил ошибки. Если код в блоке Try встречает ошибку, ошибка добавляется в переменную $Error и затем передается в блок Catch.

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

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

Перехват неуказанных ошибок (все ошибки) с помощью параметра PowerShell ErrorAction

A simple Try statement contains a Try and a Catch block. The Finally block is optional.

Например, чтобы перехватить неуказанное исключение, параметр Catch должен быть пустым. Приведенный ниже пример кода использует тот же скрипт, что и в разделе Переменная $ErrorActionPreference, но измененный для использования блоков Try Catch.

Как видно из приведенного ниже кода, на этот раз оператор foreach находится внутри блока Try. Затем блок Catch содержит код для отображения строки An Error Occurred, если произошла ошибка. Код в блоке Finally просто очищает переменную $Error.

$file_list = Get-Content .\filelist.txt
try {
    foreach ($file in $file_list) {
        Write-Output "Reading file $file"
        Get-Content $file -ErrorAction STOP
    }
}
catch {
    Write-Host "An Error Occured" -ForegroundColor RED
}
finally {
    $Error.Clear()
}

Код выше, после запуска в PowerShell, выдаст следующий вывод, показанный ниже.

Script terminated when an error occurred

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

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

Ниже приведен измененный код, в частности код внутри блока Catch, чтобы отобразить сообщение об исключении из текущей ошибки, которая была передана по каналу передачи данных – $PSItem.Exception.Message

$file_list = Get-Content .\filelist.txt
try {
    foreach ($file in $file_list) {
        Write-Output "Reading file $file"
        Get-Content $file -ErrorAction STOP
    }
}
catch {
    Write-Host $PSItem.Exception.Message -ForegroundColor RED
}
finally {
    $Error.Clear()
}

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

Script terminated with a descriptive error message

Перехват конкретных ошибок

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

Как определить тип ошибки? Проверьте значение TypeName свойства Exception последней ошибки. Например, чтобы найти тип ошибки из предыдущего примера, используйте эту команду:

$Error[0].Exception | Get-Member

Результат выполнения кода выше будет выглядеть как на скриншоте ниже. Как видите, отображается значение TypeNameSystem.Management.Automation.ItemNotFoundException.

Getting the error TypeName value

Теперь, когда вы знаете тип ошибки, который вам нужно перехватить, измените код так, чтобы он ловил именно эту ошибку. Как вы видите из измененного кода ниже, теперь есть два блока Catch. Первый блок Catch перехватывает конкретный тип ошибки (System.Management.Automation.ItemNotFoundException). В то время как второй блок Catch содержит общее сообщение об ошибке.

$file_list = Get-Content .\filelist.txt
try {
    foreach ($file in $file_list) {
        Write-Output "Reading file $file"
        Get-Content $file -ErrorAction STOP
    }
}
catch [System.Management.Automation.ItemNotFoundException]{
    Write-Host "The file $file is not found." -ForegroundColor RED
}
catch {
    Write-Host $PSItem.Exception.Message -ForegroundColor RED
}
finally {
    $Error.Clear()
}

На скриншоте ниже показан вывод измененного кода выше.

Script terminated with a specific error message

Заключение

В этой статье вы узнали ошибках в PowerShell, их свойствах и о том, как можно определить конкретный тип ошибки. Вы также узнали разницу между переменной $ErrorActionPreference и параметром ErrorAction в PowerShell и как это влияет на обработку не завершающих ошибок.

Вы также узнали, как использовать блоки Try Catch Finally в PowerShell для обработки ошибок, будь то конкретные ошибки или общий подход к их обработке.

Приведенные в этой статье примеры демонстрируют только основы работы блоков Try Catch Finally. Полученные знания, которые, я надеюсь, вы получили из этой статьи, должны дать вам отправную точку для применения обработки ошибок в ваших сценариях.

Дополнительное чтение

Source:
https://adamtheautomator.com/powershell-try-catch/