Como capturar erros em scripts Bash no Linux
Publicados: 2022-08-27Por 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.
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
Quando executamos o script, vemos a mensagem de erro esperada.
./bad_command.sh
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 $?
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.
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
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
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
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