Domine Blocos Try Catch no PowerShell com Exemplos

Já executou alguma vez um script ou cmdlet do PowerShell e se deparou com uma parede de texto gritante – em vermelho – como a mostrada abaixo?

Example of errors in PowerShell

Os erros podem se tornar avassaladores e confusos. E, acima de tudo, os erros frequentemente são difíceis de ler, o que torna quase impossível determinar o que deu errado e onde no script.

Felizmente, você tem algumas opções no PowerShell para melhorar isso por meio do tratamento de erros. Usando o tratamento de erros, os erros podem ser filtrados e exibidos de maneira que seja mais fácil compreendê-los. E entender o erro facilita a adição de mais lógica ao tratamento de erros.

Neste artigo, você aprenderá sobre erros no PowerShell e como podem ser interceptados para realizar o tratamento de erros usando os blocos Try Catch (e finally) do PowerShell.

Compreendendo Como os Erros Funcionam no PowerShell

Antes de mergulhar no tratamento de erros, vamos primeiro abordar alguns conceitos sobre erros no PowerShell. Compreender os erros pode levar a estratégias melhores de tratamento de erros.

A Variável Automática $Error

No PowerShell, existem muitas variáveis automáticas, e uma delas é a variável automática $Error. O PowerShell usa a variável $Error para armazenar todos os erros encontrados na sessão. A variável $Error é uma matriz de erros ordenados pelos mais recentes.

Quando você abre uma sessão do PowerShell pela primeira vez, a variável $Error está vazia. Você pode verificar isso chamando a variável $Error.

The $Error variable is empty

Como você pode ver, a variável $Error começa vazia. Mas, uma vez que um erro é gerado, o erro será adicionado e armazenado na variável $Error.

No exemplo abaixo, o erro é gerado ao obter intencionalmente um nome de serviço que não existe.

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

Como você pode ver na saída acima, o erro gerado foi adicionado à variável $Error.

A variável $Error contém uma coleção de erros gerados na sessão do PowerShell. Cada erro pode ser acessado chamando sua posição na matriz. O erro mais recente estará sempre no índice 0.

Por exemplo, o erro mais recente pode ser obtido usando $Error[0].

As Propriedades do Objeto $Error

Como tudo no PowerShell é um objeto, a variável $Error é um objeto, e objetos possuem propriedades. Ao encaminhar a variável $Error para o cmdlet Get-Member, você deve ver a lista das propriedades disponíveis.

$Error | Get-Member
The $Error object properties

Para determinar a razão do erro, você pode visualizar o conteúdo da propriedade InvocationInfo usando o comando abaixo.

$Error[0].InvocationInfo
The InvocationInfo property

Agora, você pode fazer o mesmo com as outras propriedades e descobrir que outras informações podem ser encontradas!

Erros Terminais

Erros terminais interrompem o fluxo de execução quando são encontrados pelo PowerShell em comparação com erros não terminais. Existem várias maneiras de ocorrer um erro terminal. Um exemplo é quando você chama um cmdlet com um parâmetro que não existe.

Como você verá na captura de tela abaixo, quando o comando Get-Process notepad é executado, o comando é válido e os detalhes do processo notepad são exibidos.

The notepad process details

Mas, quando um parâmetro que não existe é usado, como Get-Process notepad -handle 251, o cmdlet exibe um erro de que o parâmetro handle não é válido. Em seguida, o cmdlet sai sem mostrar os detalhes do processo notepad.

Error is thrown because the parameter is invalid

Erros Não Terminais

Erros não terminais são erros que não interrompem a execução do script ou comando. Por exemplo, confira o código abaixo. Este código obtém a lista de nomes de arquivo do arquivo fileslist.txt. Em seguida, o script passa por cada nome de arquivo, lê o conteúdo de cada arquivo e o exibe na tela.

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

O conteúdo do arquivo filelist.txt são os nomes de arquivo mostrados na lista abaixo.

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

Mas e se o File_6.log na verdade não existir? Quando você executa o código, espera que ocorra um erro porque o script não consegue encontrar o File_6.log. Você verá uma saída semelhante mostrada abaixo.

Example of non-terminating error

Como você pode ver na captura de tela do resultado acima, o script conseguiu ler os cinco primeiros arquivos da lista, mas ao tentar ler o arquivo File_6.txt, um erro é retornado. O script então continuou a ler o restante dos arquivos antes de sair. Ele não terminou.

A Variável $ErrorActionPreference

Até agora, você aprendeu sobre os erros terminais e não terminais e como eles diferem um do outro. Mas você sabia que um erro não terminal pode ser forçado a ser tratado como um erro terminal?

O PowerShell tem um conceito chamado de variáveis de preferência. Essas variáveis são usadas para alterar o comportamento do PowerShell de muitas maneiras diferentes. Uma dessas variáveis é chamada de $ErrorActionPreference.

A variável $ErrorActionPreference é usada para alterar a forma como o PowerShell trata os erros não terminais. Por padrão, o valor de $ErrorActionPreference é definido como Continue. Mudar o valor da variável $ErrorActionPreference para STOP força o PowerShell a tratar todos os erros como erros terminais.

Use o código abaixo para alterar o valor de $ErrorActionPreference.

$ErrorActionPreference = "STOP"

Para aprender mais sobre outros valores válidos da variável $ErrorActionPreference, visite PowerShell ErrorActionPreference.

Agora, volte ao exemplo usado na seção Erros Não Terminativos deste artigo. O script pode ser modificado para incluir a alteração em $ErrorActionPreference como o código mostrado abaixo:

# Defina o valor de $ErrorActionPreference como STOP
$ErrorActionPreference = "STOP"
$file_list =  Get-Content .\filelist.txt
foreach ($file in $file_list) {
    Write-Output "Reading file $file"
    Get-Content $file
}

Executar o código modificado acima se comportará de maneira diferente do que antes quando o valor de $ErrorActionPreference é definido como o valor padrão de Continue.

Forcing a terminating error using the $ErrorActionPreference variable

Como você pode ver na captura de tela do resultado acima, o script conseguiu ler os cinco primeiros arquivos da lista, mas quando tentou ler o arquivo File_6.txt, um erro é retornado porque o arquivo não foi encontrado. Em seguida, o script foi encerrado, e o restante dos arquivos não é lido.

O valor de $ErrorActionPreference é válido apenas na sessão atual do PowerShell. Ele é redefinido para o valor padrão assim que uma nova sessão do PowerShell é iniciada.

O Parâmetro Comum ErrorAction

Se o valor de $ErrorActionPreference for aplicado à sessão do PowerShell, o parâmetro ErrorAction se aplica a qualquer cmdlet que suporte parâmetros comuns. O parâmetro ErrorAction aceita os mesmos valores que a variável $ErrorActionPreference.

O valor do parâmetro ErrorAction tem precedência sobre o valor de $ErrorActionPreference.

Vamos voltar e usar o mesmo código do exemplo anterior. Mas, desta vez, o parâmetro ErrorAction é adicionado à linha Get-Content.

# Define o valor de $ErrorActionPreference como padrão (CONTINUAR)
$ErrorActionPreference = "CONTINUE"
$file_list =  Get-Content .\filelist.txt
foreach ($file in $file_list) {
    Write-Output "Reading file $file"
		# Use o parâmetro comum -ErrorAction
		Get-Content $file -ErrorAction STOP
}

Depois de executar o código modificado, você verá que, mesmo que $ErrorActionPreference seja definido como Continuar, o script ainda terminou ao encontrar um erro. O script terminou porque o valor do parâmetro ErrorAction no Get-Content está definido como PARAR.

Forcing a terminating error using the PowerShell ErrorAction parameter

Usando Blocos PowerShell Try Catch

Neste ponto, você aprendeu sobre os erros do PowerShell e como a variável $ErrorActionPreference e os parâmetros ErrorAction do PowerShell funcionam. Agora é hora de aprender sobre o bom conteúdo – os blocos Try Catch Finally do PowerShell.

Blocos try catch do PowerShell (e bloco opcional finally) são uma maneira de lançar uma rede ao redor de um trecho de código e capturar quaisquer erros que retornem.

O código abaixo mostra a sintaxe da instrução Try.

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

O bloco Try contém o código que você deseja que o PowerShell “tente” e monitore em busca de erros. Se o código no bloco Try encontrar um erro, o erro é adicionado à variável $Error e então passado para o bloco Catch.

O bloco Catch contém as ações a serem executadas quando ele recebe um erro do bloco Try. Pode haver vários blocos Catch em uma instrução Try.

O bloco Finally contém o código que será executado no final da instrução Try. Este bloco é executado independentemente de um erro ter sido encontrado ou não.

Capturando Erros Não-Específicos (Catch-All) com ErrorAction do PowerShell

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

Por exemplo, para capturar uma exceção não específica, o parâmetro Catch deve estar vazio. O código de exemplo abaixo está usando o mesmo script que foi usado na seção A Variável $ErrorActionPreference mas modificado para usar os blocos Try Catch.

Como você pode ver no código abaixo, desta vez, a instrução foreach está dentro do bloco Try. Em seguida, um bloco Catch contém o código para exibir a string Ocorreu um Erro se um erro ocorrer. O código no bloco Finally apenas limpa a variável $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()
}

O código acima, após ser executado no PowerShell, fornecerá esta saída mostrada abaixo.

Script terminated when an error occurred

A saída acima mostra que o script encontrou um erro, executou o código dentro do bloco Catch e depois terminou.

O erro foi tratado, que era o objetivo do tratamento de erros. No entanto, o erro exibido foi muito genérico. Para mostrar um erro mais descritivo, você poderia acessar a propriedade Exception do erro que foi passado pelo bloco Try.

O código abaixo foi modificado, especificamente o código dentro do bloco Catch, para exibir a mensagem de exceção do erro atual que foi passado pelo pipeline – $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()
}

Desta vez, quando o código modificado acima é executado, a mensagem exibida é muito mais descritiva.

Script terminated with a descriptive error message

Capturando Erros Específicos

Há momentos em que um tratamento de erro genérico não é a abordagem mais apropriada. Talvez você queira que seu script execute uma ação que dependa do tipo de erro encontrado.

Como você determina o tipo de erro? Verificando o valor TypeName da propriedade Exception do último erro. Por exemplo, para encontrar o tipo de erro do exemplo anterior, use este comando:

$Error[0].Exception | Get-Member

O resultado do código acima se pareceria com a captura de tela abaixo. Como você pode ver, o valor TypeName é exibido – System.Management.Automation.ItemNotFoundException.

Getting the error TypeName value

Agora que você sabe o tipo de erro que precisa interceptar, modifique o código para capturá-lo especificamente. Como você pode ver no código modificado abaixo, agora existem dois blocos Catch. O primeiro bloco Catch intercepta um tipo específico de erro (System.Management.Automation.ItemNotFoundException). Em contraste, o segundo bloco Catch contém a mensagem de erro genérica, que abrange todos os casos.

$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()
}

A captura de tela abaixo mostra a saída do código modificado acima.

Script terminated with a specific error message

Conclusão

Neste artigo, você aprendeu sobre erros no PowerShell, suas propriedades e como pode determinar o tipo específico de erro. Você também aprendeu a diferença entre como a variável $ErrorActionPreference e o parâmetro ErrorAction do PowerShell afetam o tratamento de erros não terminativos.

Você também aprendeu como usar os blocos Try Catch Finally do PowerShell para lidar com erros, seja para erros específicos ou uma abordagem geral de captura.

Os exemplos mostrados neste artigo apenas demonstram o básico de como os blocos Try Catch Finally funcionam. O conhecimento que espero que você tenha adquirido neste artigo deve fornecer a base para começar a aplicar tratamento de erros em seus scripts.

Leitura adicional

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