Поиск по сайту:

Как использовать Try, Catch, Finally в PowerShell


Ошибки в вашем скрипте PowerShell могут остановить выполнение вашего скрипта, но иногда это совершенно не нужно. Блоки Try Catch в PowerShell помогают вам правильно обрабатывать эти ошибки.

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

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

С помощью конструкции Try Catch в PowerShell мы можем обработать весь список и написать правильное сообщение об ошибке (или даже отправить электронное письмо), когда что-то пойдет не так.

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

Powershell Попытка Ловить Наконец

Давайте сначала рассмотрим основы блока Try Catch Finally. Блок Try Catch в Powershell всегда состоит из одного блока Try и по крайней мере одного блока Catch. Блок Finally является необязательным, код в этом блоке всегда будет выполняться, независимо от результата блока Try.

try
{
    # Try something that could cause an error
    1/0
}
catch
{
    # Catch any error
    Write-Host "An error occurred"
}
finally
{
    # [Optional] Run this part always
    Write-Host "cleaning up ..."
}

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

Если мы рассмотрим следующий (упрощенный) пример для обновления должностей:

try{
	# Find the user to update
	$ADUser = Get-AzureAdUser -SearchString $user.name

	# Update the job title
	Set-AzureAdUser -ObjectId $ADUser.ObjectId -JobTitle $user.jobtitle

	# Send an email that the job title is updated
	Send-MailMessage -SmtpServer $smtp.address -To $user.mail -From $smtp.from -Subject $smtp.subject -Body "Your jobtitle is updated"
}
catch{
	Write-Host ("Failed to update " + $($user.name)) -ForegroundColor Red
}

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

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

Блок Finally в PowerShell

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

Обработка завершившихся и незавершившихся ошибок

Когда речь идет о выявлении ошибок в PowerShell, есть одна действительно важная вещь — это нетерминальные ошибки. Это ошибки, которые не завершат (не остановят) выполнение скрипта. Такие ошибки по умолчанию нельзя поймать с помощью блока catch.

Большинство командлетов в PowerShell не являются завершительными. Если вы используете их неправильно, они выведут ошибку, которую вы увидите красным цветом в консоли, но не остановят выполнение скрипта. Причина этого — значение по умолчанию для ErrorAction в вашем профиле PowerShell, которое установлено на Continue.

To show your default error action type
$ErrorActionPreference

Рассмотрим следующий пример: открытие несуществующего каталога в PowerShell с использованием блока Try Catch:

try {
    dir "c:\some\non-existing\path"
    
}
catch {
    Write-host "Directory does not exist"
}

Вы ожидаете увидеть «Каталог не существует», но вместо этого вы получаете обычное красное сообщение об ошибке. Причина этого в том, что несуществующий путь не является критической ошибкой, и действие по умолчанию — продолжить.

Чтобы поймать ошибку, вам нужно добавить параметр -ErrorAction Stop после вашего действия.

try {
    dir "c:\some\non-existing\path" -ErrorAction stop
    
}
catch {
    Write-host "Directory does not exist"
}

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

$ErrorActionPreference = "Stop"

Powershell Попытка Поймать Исключение

Исключения в PowerShell позволяют вам еще лучше обрабатывать ошибки. До сих пор мы просто использовали простой блок catch, который в основном ловит любые ошибки. Это хороший способ начать, но если вы хотите еще больше улучшить обработку ошибок в PowerShell, то вы можете использовать Исключения.

Как я упоминал в начале, блок Try Catch должен содержать по крайней мере один блок catch. Это означает, что мы можем иметь несколько блоков catch для обработки разных ошибок и различного способа их обработки.

Если мы рассмотрим следующий пример с обновлением должностей:

try{
	# Find the user to update
	$ADUser = Get-AzureAdUser -ObjectId $user.UserPrincipalName -ErrorAction Stop

	# Update the job title
	Set-AzureAdUser -ObjectId $ADUser.ObjectId -JobTitle $user.jobtitle -ErrorAction Stop
}
catch [Microsoft.Open.Azure.AD.CommonLibrary.AadNeedAuthenticationException]{
	# Catch a connection error
	Write-Warning "You need to Connect to AzureAD first"
}
catch [Microsoft.Open.AzureAD16.Client.ApiException] {
	# Catch the when we are unable to find the user
	Write-Warning "Unable to find the user"
}
Catch {
	Write-Warning "An other error occured"
}

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

Сложная часть здесь заключается в поиске исключения, которое вам нужно поймать. Единственный способ их найти - это запустить ваш скрипт и убедиться, что вы вызываете ошибку, которую хотите поймать. Затем вы можете найти исключение в сообщении об ошибке или в переменной $Error.

Как вы можете видеть на скриншоте, мы можем найти исключение аутентификации в сообщении об ошибке (1). Другой вариант — посмотреть на переменную $Error. Если вы выполните $Error[0].Exception.GetType().FullName сразу после возникновения ошибки, то получите полное имя исключения, которое можете использовать в блоке catch.

Переменная ошибки Powershell

Давайте немного подробнее объясним переменную ошибок PowerShell. Когда в PowerShell возникает ошибка, она добавляется в переменную $error. Эта переменная будет содержать все ошибки, произошедшие за время вашей сессии PowerShell.

Вместо $error вы также можете использовать $._ или $PSitem внутри блока catch, чтобы отобразить подробности об ошибке.

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

Поиск местоположения ошибки

Когда вы пишете более крупный скрипт, полезно знать точное местоположение функции, которая вызывает ошибку. В переменной ошибки вы найдете ScriptStackTrace. Это выведет точное место ошибки и её источник.

Возьмите следующий пример:

$ErrorActionPreference = "Stop"


Function OpenPath($path) {
    try {
        dir $path 
    }
    catch {
        Write-host "Directory does not exist" -ForegroundColor Red
        Write-Host $_.ScriptStackTrace
    }
}

OpenPath("c:\some\non-existing\path")

Когда вы выполните этот код, вы получите следующий трейс стека:

Как вы можете видеть, ошибка произошла на строке 6 в функции OpenPath, которая была вызвана скриптом на строке 15.

Этот вид информации действительно может помочь вам в отладке ваших скриптов.

Показать правильные сообщения об ошибках

Вы можете написать свои собственные сообщения об ошибках в блоках Catch PowerShell, но иногда сообщения об исключениях более чем достаточно.

Function OpenPath($path) {
    try {
        dir $path -ErrorAction Stop
    }
    catch {
        Write-Host $_.Exception.Message -ForegroundColor Red
    }
}

#Outputs:
Cannot find path 'C:\some\non-existing\path' because it does not exist.

Ошибки подсчета в PowerShell

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

$Error.count

Очистка переменной ошибок PowerShell

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

$Error.clear()

Завершение

Блоки Try Catch в PowerShell помогают вам писать лучшие скрипты, скрипты, которые делают то, что вы хотите, даже если что-то пойдет не так. Самая сложная часть - это написание хороших блоков catch, потому что вам нужно разобраться, что может пойти не так в вашем скрипте.

Я надеюсь, что эта статья помогла вам начать работу с блоками Try-Catch. Если у вас есть какие-либо вопросы, просто оставьте комментарий ниже.

Вам также могут понравиться следующие статьи:

  • Подключитесь к Exchange Online с помощью PowerShell
  • Включить MFA для пользователя Office 365 с помощью PowerShell