Domine Blocos Try Catch no PowerShell com Exemplos

Já executou algum 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 muitas vezes são difíceis de ler, o que torna praticamente impossível determinar o que e onde o script deu errado.

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 forma que seja mais fácil entender. E, entender o erro facilita a adição de mais lógica ao tratamento de erros.

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

Entendendo Como os Erros Funcionam no PowerShell

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

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 é um array de erros ordenados pelo mais recente.

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 por tentar obter deliberadamente 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 no array. O erro mais recente estará sempre no índice 0.

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

As Propriedades do Objeto $Error

Uma vez que tudo no PowerShell é um objeto, a variável $Error é um objeto, e objetos têm 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ê poderia fazer o mesmo com as outras propriedades e descobrir que outras informações você pode encontrar!

Erros Terminais

Erros de terminação interrompem o fluxo de execução quando encontrados pelo PowerShell em comparação com erros não terminativos. Existem várias maneiras pelas quais um erro de término pode ocorrer. Um exemplo é quando você chama um cmdlet com um parâmetro que não existe.

Como você pode 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

No entanto, quando um parâmetro que não existe é utilizado, como Get-Process notepad -handle 251, o cmdlet exibe um erro informando que o parâmetro handle não é válido. Em seguida, o cmdlet é encerrado sem mostrar os detalhes do processo notepad.

Error is thrown because the parameter is invalid

Erros não Terminativos

Erros não terminativos são erros que não interrompem a execução do script ou comando. Por exemplo, veja o código abaixo. Este código obtém a lista de nomes de arquivo do arquivo fileslist.txt. Em seguida, o script percorre 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 File_6.log na verdade não existisse? Quando você executa o código, espera-se que ocorra um erro, pois o script não pode 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 quando tentou ler o arquivo File_6.txt, um erro foi retornado. O script então continuou a ler o restante dos arquivos antes de encerrar. Não terminou.

A variável $ErrorActionPreference

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

O PowerShell tem um conceito chamado 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 maneira como o PowerShell trata os erros não terminantes. Por padrão, o valor de $ErrorActionPreference é definido como Continue. Alterar o valor da variável $ErrorActionPreference para STOP força o PowerShell a tratar todos os erros como erros terminantes.

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

$ErrorActionPreference = "STOP"

Para obter mais informações sobre outros valores válidos da variável $ErrorActionPreference, visite PowerShell ErrorActionPreference.

Agora, volte ao exemplo usado na seção de Erros Não Terminativos neste artigo. O script pode ser modificado para incluir a alteração em $ErrorActionPreference, como mostrado no código 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
}

A execução do código modificado acima terá um comportamento diferente do anterior, quando o valor de $ErrorActionPreference estiver definido como o valor padrão de Continue.

Forcing a terminating error using the $ErrorActionPreference variable

Conforme 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 foi lido.

O valor de $ErrorActionPreference é válido apenas na sessão atual do PowerShell. Ele é redefinido para o valor padrão quando 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 faz.

O 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 para o padrão (CONTINUE)
$ErrorActionPreference = "CONTINUE"
$file_list =  Get-Content .\filelist.txt
foreach ($file in $file_list) {
    Write-Output "Reading file $file"
		# Usa o parâmetro comum -ErrorAction
		Get-Content $file -ErrorAction STOP
}

Depois de executar o código modificado, você verá que, mesmo que o $ErrorActionPreference esteja definido como Continue, o script ainda é encerrado quando encontra um erro. O script é encerrado porque o valor do parâmetro ErrorAction do PowerShell em Get-Content está definido como STOP.

Forcing a terminating error using the PowerShell ErrorAction parameter

Usando Blocos Try Catch do PowerShell

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

Bloco de try catch do PowerShell (e bloco opcional finally) é uma maneira de envolver um trecho de código e capturar quaisquer erros que ocorram.

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 quanto a 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 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. Esse bloco é executado independentemente de um erro ter ocorrido 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 Variável $ErrorActionPreference, mas modificado para usar os blocos Try Catch.

Como você pode ver no código abaixo, desta vez, 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 acontecer. 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á a 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 então terminou.

O erro foi tratado, que era o ponto do tratamento de erros. No entanto, o erro exibido foi muito genérico. Para mostrar um erro mais descritivo, você pode 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 geral não é a abordagem mais apropriada. Talvez você queira que seu script execute uma ação que dependa do tipo de erro encontrado.

Como determinar 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ê conhece 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, para 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 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 terminais.

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 abrangente.

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 lhe fornecer as bases para começar a aplicar o tratamento de erros em seus scripts.

Leitura adicional

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