Como capturar erros em scripts Bash no Linux

Publicados: 2022-08-27
Laptop Linux mostrando um prompt bash
fatmawati achmad zaenuri/Shutterstock.com

Por padrão, um script Bash no Linux relatará um erro, mas continuará em execução. Mostramos a você como lidar com erros para que você possa decidir o que precisa acontecer em seguida.

Tratamento de erros em scripts

O tratamento de erros faz parte da programação. Mesmo se você escrever um código sem falhas, ainda poderá encontrar condições de erro. O ambiente em seu computador muda com o tempo, conforme você instala e desinstala software, cria diretórios e executa upgrades e atualizações.

Personalizando erros de validação de parâmetro no PowerShell
RELACIONADO Personalizando erros de validação de parâmetro no PowerShell

Por exemplo, um script que costumava ser executado sem problemas pode ter dificuldades se os caminhos do diretório forem alterados ou as permissões forem alteradas em um arquivo. A ação padrão do shell Bash é imprimir uma mensagem de erro e continuar a executar o script. Este é um padrão perigoso.

Se a ação que falhou for crítica para algum outro processamento ou ação que acontece posteriormente em seu script, essa ação crítica não será bem-sucedida. Quão desastroso isso se torna, depende do que seu script está tentando fazer.

Um esquema mais robusto detectaria erros e permitiria que o script funcionasse se fosse necessário desligar ou tentar remediar a condição de falha. Por exemplo, se um diretório ou arquivo estiver ausente, pode ser satisfatório que o script os recrie.

Se o script encontrou um problema do qual não pode se recuperar, ele pode ser encerrado. Se o script precisar ser encerrado, ele poderá executar qualquer limpeza necessária, como remover arquivos temporários ou gravar a condição de erro e o motivo do encerramento em um arquivo de log.

Detectando o status de saída

Comandos e programas geram um valor que é enviado ao sistema operacional quando são encerrados. Isso é chamado de status de saída. Tem um valor de zero se não houver erros, ou algum valor diferente de zero se ocorreu um erro.

Podemos verificar o status de saída - também conhecido como código de retorno - dos comandos que o script usa e determinar se o comando foi bem-sucedido ou não.

No Bash, zero equivale a verdadeiro. Se a resposta do comando não for verdadeira, sabemos que ocorreu um problema e podemos tomar as medidas apropriadas.

Copie este script em um editor e salve-o em um arquivo chamado “bad_command.sh”.

 #!/bin/bash

if (! bad_command); então
  echo "bad_command sinalizou um erro."
  saída 1
fi

Você precisará tornar o script executável com o comando chmod . Esta é uma etapa necessária para tornar qualquer script executável, portanto, se você quiser experimentar os scripts em sua própria máquina, lembre-se de fazer isso para cada um deles. Substitua o nome do script apropriado em cada caso.

 chmod +x bad_command.sh 

Tornando um script executável usando chmod

Quando executamos o script, vemos a mensagem de erro esperada.

 ./bad_command.sh 

Verificando o status de saída de um comando para determinar se houve um erro

Não existe um comando como “bad_command”, nem é o nome de uma função dentro do script. Ele não pode ser executado, então a resposta não é zero. Se a resposta não for zero — o ponto de exclamação é usado aqui como o operador lógico NOT — o corpo da instrução if é executado.

Em um script do mundo real, isso pode encerrar o script, o que nosso exemplo faz, ou pode tentar remediar a condição de falha.

Pode parecer que a linha exit 1 é redundante. Afinal, não há mais nada no script e ele terminará de qualquer maneira. Mas usar o comando exit nos permite passar um status de saída de volta ao shell. Se nosso script for chamado de dentro de um segundo script, esse segundo script saberá que esse script encontrou erros.

Você pode usar o operador OR lógico com o status de saída de um comando e chamar outro comando ou uma função em seu script se houver uma resposta diferente de zero do primeiro comando.

 comando_1 || comando_2

Isso funciona porque o primeiro comando é executado OR o segundo. O comando mais à esquerda é executado primeiro. Se for bem-sucedido, o segundo comando não será executado. Mas se o primeiro comando falhar, o segundo comando será executado. Assim, podemos estruturar o código assim. Este é "lógico-ou./sh."

 #!/bin/bash

error_handler()
{
  echo "Erro: ($?) $1"
  saída 1
}

bad_command || error_handler "bad_command falhou, Linha: ${LINENO}"

Definimos uma função chamada error_handler . Isso imprime o status de saída do comando com falha, mantido na variável $? e uma linha de texto que é passada para ele quando a função é chamada. Isso é mantido na variável $1 . A função termina o script com um status de saída de um.

O script tenta executar bad_command que obviamente falha, então o comando à direita do operador lógico OR , || , É executado. Isso chama a função error_handler e passa uma string que nomeia o comando que falhou e contém o número da linha do comando com falha.

Executaremos o script para ver a mensagem do manipulador de erros e, em seguida, verificaremos o status de saída do script usando echo.

 ./logical-or.sh
 eco $? 

Usando o operador lógico OR para chamar o manipulador de erros em um script

Nossa pequena função error_handler fornece o status de saída da tentativa de executar bad_command , o nome do comando e o número da linha. Esta é uma informação útil quando você está depurando um script.

O status de saída do script é um. O status de saída 127 relatado por error_handler significa “comando não encontrado”. Se quiséssemos, poderíamos usar isso como o status de saída do script, passando-o para o comando exit .

Outra abordagem seria expandir error_handler para verificar os diferentes valores possíveis do status de saída e executar ações diferentes de acordo, usando este tipo de construção:

 exit_code=$?

if [ $exit_code -eq 1]; então
  echo "Operação não permitida"

elif [ $exit_code -eq 2 ]; então
  echo "Uso indevido de shell embutido"
.
.
.
elif [ $status -eq 128 ]; então
  echo "Argumento inválido"
fi

Usando set para forçar uma saída

Se você sabe que deseja que seu script saia sempre que houver um erro, você pode forçá-lo a fazer isso. isso significa que você abre mão da chance de qualquer limpeza — ou qualquer dano adicional também — porque seu script termina assim que detecta um erro.

Para fazer isso, use o comando set com a opção -e (erro). Isso diz ao script para sair sempre que um comando falhar ou retornar um código de saída maior que zero. Além disso, usar a opção -E garante que a detecção de erros e o trapping funcionem nas funções do shell.

Como usar set e pipefail em scripts Bash no Linux
RELACIONADO Como usar set e pipefail em scripts Bash no Linux

Para também capturar variáveis ​​não inicializadas, adicione a opção -u (unset). Para certificar-se de que os erros sejam detectados em sequências canalizadas, inclua a opção -o pipefail . Sem isso, o status de saída de uma sequência de comandos canalizada é o status de saída do comando final na sequência. Um comando com falha no meio da sequência canalizada não seria detectado. A opção -o pipefail deve vir na lista de opções.

A sequência a ser adicionada ao topo do seu script é:

 set -Eeuo pipefail

Aqui está um pequeno script chamado “unset-var.sh”, com uma variável unset nele.

 #!/bin/bash

set -Eeou pipefail

echo "$unset_variable"

echo "Nós vemos esta linha?"

Quando executamos o script, a unset_variable é reconhecida como uma variável não inicializada e o script é finalizado.

 ./unset-var.sh 

Usando o comando set em um script para encerrar o script se ocorrer um erro

O segundo comando echo nunca é executado.

Usando trap com erros

O comando Bash trap permite nomear um comando ou uma função que deve ser chamada quando um sinal específico é gerado. Normalmente, isso é usado para capturar sinais como SIGINT , que é gerado quando você pressiona a combinação de teclas Ctrl+C. Este script é “sigint.sh”.

 #!/bin/bash

trap "echo -e '\nTerminado por Ctrl+c'; sair" SIGINT

contador=0

enquanto verdadeiro
Faz 
  echo "Número do loop:" $((++contador))
  dormir 1
feito

O comando trap contém um comando echo e o comando exit . Ele será acionado quando o SIGINT for gerado. O resto do script é um loop simples. Se você executar o script e pressionar Ctrl+C, verá a mensagem da definição de trap e o script será encerrado.

 ./sigint.sh 

Usando trap em um script para capturar Ctrl+c

Podemos usar trap com o sinal ERR para capturar erros à medida que eles ocorrem. Estes podem então ser alimentados a um comando ou função. Este é "trap.sh". Estamos enviando notificações de erro para uma função chamada error_handler .

 #!/bin/bash

trap 'error_handler $? $LINENO' ERR

error_handler() {
  echo "Erro: ($1) ocorreu em $2"
}

a Principal() {
  echo "Dentro da função main()"
  bad_command
  segundo
  terceiro
  sair $?
}

segundo() {
  echo "Após a chamada para main()"
  echo "Dentro da função second()"
}

terceiro() {
  echo "Dentro da função third()"
}

a Principal

A maior parte do script está dentro da função main , que chama a second e a third funções. Quando um erro é encontrado—neste caso, porque bad_command não existe—a instrução trap direciona o erro para a função error_handler . Ele passa o status de saída do comando com falha e o número da linha para a função error_handler .

 ./trap.sh 

Usando trap com ERR para capturar erros em um script

Nossa função error_handler simplesmente lista os detalhes do erro na janela do terminal. Se desejar, você pode adicionar um comando exit à função para que o script seja encerrado. Ou você pode usar uma série de if/elif/fi para realizar ações diferentes para erros diferentes.

Pode ser possível corrigir alguns erros, outros podem exigir que o script pare.

Uma dica final

Capturar erros geralmente significa antecipar as coisas que podem dar errado e colocar um código para lidar com essas eventualidades, caso surjam. Isso além de garantir que o fluxo de execução e a lógica interna do seu script estejam corretos.

Se você usar este comando para executar seu script, o Bash mostrará uma saída de rastreamento à medida que o script é executado:

 bash -x seu-script.sh

O Bash grava a saída de rastreamento na janela do terminal. Ele mostra cada comando com seus argumentos - se houver algum. Isso acontece após os comandos serem expandidos, mas antes de serem executados.

Pode ser uma tremenda ajuda no rastreamento de bugs indescritíveis.

RELACIONADO: Como validar a sintaxe de um script Bash do Linux antes de executá-lo