Como capturar erros em scripts Bash no Linux

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, à medida que 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 chmodcomando. 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 NOToperador lógico — o corpo da ifinstrução é executado.

Recomendado:  O que diabos a Valve ainda faz mais (além de pegar nosso dinheiro)

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 exit 1linha é redundante. Afinal, não há mais nada no script e ele terminará de qualquer maneira. Mas usar o exitcomando 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 lógico ORcom 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 executa ORo 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 o bad_commandque obviamente falha, então o comando à direita do ORoperador lógico, ||, é executado. Isso chama a error_handlerfunção e passa uma string que nomeia o comando que falhou e contém o número da linha do comando com falha.

Recomendado:  Como alterar as cores da lâmpada inteligente na tela do Google Home Hub

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 error_handlerfunção fornece o status de saída da tentativa de execução 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_handlermeio de “comando não encontrado”. Se quiséssemos, poderíamos usar isso como o status de saída do script, passando-o para o exitcomando.

Outra abordagem seria expandir error_handlerpara verificar os diferentes valores possíveis do status de saída e executar diferentes ações 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 setcomando com a -eopção (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, o uso da -Eopção 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 -uopção (unset). Para certificar-se de que os erros sejam detectados em sequências canalizadas, adicione a -o pipefailopção. 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 -o pipefailopção 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 echocomando nunca é executado.

Recomendado:  Como os hologramas funcionam no palco?

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 o SIGINTque é 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 trapcomando contém um echocomando e o exitcomando. Ele será acionado quando SIGINTfor levantado. O resto do script é um loop simples. Se você executar o script e pressionar Ctrl+C, verá a mensagem da trapdefinição e o script será encerrado.

./sigint.sh

Usando trap em um script para capturar Ctrl+c

Podemos usar trapcom o ERRsinal para detectar 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 mainfunção, que chama as funções seconde third. Quando um erro é encontrado—neste caso, porque bad_commandnão existe—a trapinstrução direciona o erro para a error_handlerfunção. Ele passa o status de saída do comando com falha e o número da linha para a error_handlerfunção.

./trap.sh

Usando trap com ERR para capturar erros em um script

Nossa error_handlerfunção simplesmente lista os detalhes do erro na janela do terminal. Se desejar, você pode adicionar um exitcomando à função para que o script seja encerrado. Ou você pode usar uma série de if/elif/fiinstruções para executar 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 .