Scripting em Bash com set -euo pipefail, trap e logging

Última atualização: 17/12/2025
autor: Isaac
  • Habilitar `set -euo pipefail` e o IFS seguro permite que os scripts bash falhem antecipadamente e evitem erros silenciosos ao lidar com códigos de saída e variáveis ​​indefinidas.
  • Utilizar armadilhas para erros (ERR) e sinais como INT ou TERM facilita o registro de falhas, a realização de limpeza controlada e a manutenção do sistema em um estado consistente.
  • Um sistema de registro centralizado com níveis e carimbos de data/hora melhora a rastreabilidade, a depuração e a integração de scripts em ambientes DevOps e CI/CD.
  • A combinação de programação defensiva, validação de entrada e boas práticas de depuração torna os scripts bash confiáveis ​​em produção.

O que são blobs binários ou proprietários no Linux?

Automatizar tarefas com Bash faz toda a diferença entre um sistema que exige monitoramento constante e um que simplesmente... Ele funciona sozinho e avisa quando algo dá errado.Em ambientes de DevOps e administração de sistemas, um bom script não se resume apenas a encadear comandos. comandosTrata-se de evitar falhas silenciosas, ser capaz de depurar rapidamente e deixar rastros claros. toras Para descobrir o que aconteceu e quando.

Quando você começa a trabalhar seriamente com scripts, descobre que o Bash, por padrão, é bastante permissivo: se algo der errado, o escrita usualmente Continuar como se nada tivesse acontecido, usando variáveis ​​vazias ou resultados incompletos.É daí que surgem histórias de terror como backups truncados (para os quais é recomendável). Sincronizar com rsync no Linux), exclusões em massa devido a rotas mal construídas ou implantações que pareciam corretas, mas falharam no meio do caminho. É por isso que aprender a usá-lo corretamente faz tanto sentido. set -euo pipefail, trap e um sistema de registro de logs decente.

Por que o Bash continua sendo fundamental no DevOps

No dia a dia do trabalho de sistemas e DevOps, o Bash continua sendo a ferramenta sempre presente: nos servidores. Linuxcontêineres, máquinas CI/CD, scripts de manutenção… Sua maior vantagem é que Você não depende da instalação de ambientes de execução ou bibliotecas externas.Com a estrutura básica que o sistema já inclui, você pode automatizar tudo, desde verificações de integridade até implantações completas.

Esse poder tem um porém: com scripts mal escritos, é muito fácil que um comando falhe e O restante do roteiro continua como se tudo tivesse corrido perfeitamente.Se você adicionar tarefas críticas a isso (backups, rotação de logs, exclusão de arquivos, execuções agendadas), o risco deixa de ser teórico: mais cedo ou mais tarde, algo vai falhar sem que você perceba.

Por isso é tão importante que, além de automatizar, você inclua isso desde o início. Tratamento de erros, depuração e registro estruturadoE é aí que entram em jogo três elementos que se repetem em todos os bons exemplos de scripts em Bash moderno: set -euo pipefail, trap e um sistema de registro consistente.

Exemplo prático: script de monitoramento com tratamento rigoroso de erros.

Um padrão muito útil é criar um script de monitoramento que colete informações importantes do sistema (CPU, memória, disco, rede, processos, logs recentes…) e que também falhar de forma segura, deixando rastros claros em um arquivo de log.Esse tipo de script pode ser usado manualmente, por meio do cron ou para dar suporte a pipelines de CI/CD.

ideia geral A ideia geral é ter um roteiro com:

  • Modo estrito ativado Use `set -euo pipefail` para interromper a execução antecipadamente e não continuar em estados inconsistentes.
  • Captura em ERR e sinais como INT ou TERM Registrar erros e limpar antes de sair.
  • Função de registro centralizada que escreve no console um arquivo com o carimbo de data/hora e o nível (INFO, WARNING, ERROR).
  • exames modulares Registros de CPU, memória, disco, rede e sistema.
  • Instalação opcional de dependências tais como sysstat, lm-sensors ou net-tools, adaptados para Debian/Ubuntu ou RHEL/CentOS/Fedora.

habitual Em um roteiro desse tipo, é comum ver algo como:

  • Variáveis ​​de cor para destacar avisos e erros no terminal (VERMELHO, VERDE, AMARELO, AZUL, NC).
  • Rotas como /var/log/system-monitor.log para registros gerais e /var/log/system-metrics.log para métricas periódicas.
  • Limiares configuráveis alerta: por exemplo, ALERT_CPU_THRESHOLD=80, ALERT_MEM_THRESHOLD=80, ALERT_DISK_THRESHOLD=85.
  • Un intervalo de monitoramento MONITOR_INTERVAL para repetir as verificações a cada X segundos se você executar o script em um loop.

Com base nessa fundação, é construído um script robusto que não apenas coleta informações, mas também Reage corretamente às falhas, deixa evidências claras e pode ser integrado a processos maiores..

Ative o modo estrito: defina -euo pipefail e IFS seguro.

A maioria dos problemas "estranhos" no Bash decorre do fato de que o shell, por padrão, Não considera grave que um comando falhe ou que uma variável não exista.Para forçar um comportamento mais seguro, o que muitas pessoas chamam de "modo Bash estrito" é frequentemente ativado.

  Como atualizar o Edge para a versão mais recente no Windows 11: um guia completo passo a passo

Os componentes básicos desse modo estrito são:

  • definir -e: faz com que o script termine assim que um comando retornar um código de saída diferente de 0, exceto em alguns contextos especiais (if, while, &&, ||…).
  • definir -u o set -o substantivo: causa um erro quando você tenta usar uma variável que não está definida, em vez de tratá-la como uma string vazia.
  • set -o pipefail: gera o código de saída em um pipeline que é o código do primeiro comando que falha, e não apenas do último.
  • IFS=$'\n\t': limita o separador de campo interno à quebra de linha e à tabulação para evitar que espaços sejam quebrados em loops sobre listas de arquivos ou similares.

A combinação de todos esses elementos resulta em algo muito comum no cabeçalho de scripts sérios:

set -euo pipefail
IFS=$'\n\t'
cabeceira segura

Isso evita situações em que um comando intermediário em um pipeline falha, mas o script continua porque o último comando retorna 0. Também evita... variáveis ​​com erros ortográficos ou indefinidas silenciosamente se tornam cadeias vazias, o que muitas vezes é a origem de caminhos incorretos ou condições lógicas que nunca são atendidas.

As armadilhas do comando `set -e`: por que ele não é mágico e pode quebrar seu script.

Embora seja muito útil, O parâmetro -e não é uma solução mágica.Seu comportamento apresenta nuances que podem ser surpreendentes se você não entender como funciona. Por exemplo, algumas construções como:

  • Comandos dentro if, while o until.
  • Expressões com && y || para controle de fluxo.
  • Algumas expansões aritméticas.

Eles não acionam a "saída imediata" que você poderia esperar. Um exemplo clássico é o de expansão aritmética com pós-incremento:

((contador++))

A expansão aritmética retorna um valor que o Bash interpreta como um código de saída. No caso de pós-incremento, quando a expressão retorna 0, ela é considerada um sucesso, mas em outras ocasiões pode retornar 1, que com O parâmetro -e faz com que o script termine imediatamente.Algo tão inocente quanto um contador dentro de um loop pode fazer com que seu script seja encerrado prematuramente.

Em contraste, construções como ((++i)) ou ((i+=1)) Eles se comportam de maneira diferente e não desencadeiam o mesmo problema. Isso demonstra que Com o comando `set -e`, você precisa conhecer bem as construções que está usando.Porque existem muitos casos limítrofes.

Portanto, diversos autores e administradores experientes recomendam o uso de Defina -e, especialmente durante os testes....como uma forma agressiva de encontrar pontos fracos, mas em produção eles preferem uma abordagem mais explícita: verificar manualmente os códigos de saída e Decida exatamente quando e como o script será interrompido..

armadilha: capturar erros e sinais para limpar e registrar corretamente.

Comando armadilha É o outro elemento principal quando falamos de scripts defensivos em Bash. Basicamente, ele permite especificar quais comandos ou funções você deseja executar quando o shell recebe um sinal ou um evento especial (como ERR ou SAÍDA).

usos típicos Os usos típicos de trap em scripts robustos são:

  • trap 'error_handling_function' ERRExecutar a lógica de tratamento de erros imediatamente quando um comando falhar.
  • armadilha 'função_de_limpeza' TERMINAL INT: reagir a sinais como CTRL+C (SIGINT) ou solicitações de parada (SIGTERM).
  • armadilha 'função_finalização' SAÍDAExecutar um bloco ao sair do script, independentemente de ter corrido bem ou não.

Imagine um script que, ao receber CTRL+C, em vez de parar abruptamente, chama uma função. limpeza que exclui arquivos temporários, fecha conexões ou deixa uma mensagem clara nos registros:

trap 'limpieza' TERM INT
function limpieza(){
echo "Ejecutando limpieza, el usuario uso CTRL + C"
# ... lógica de limpieza ...
}
ação preventiva

Esse padrão é especialmente útil em processos longos (backups, ETL, implantações) onde você tem interesse para poder abortar de forma controlada sem deixar o sistema em um estado inconsistente..

O que você pode e não pode capturar com uma armadilha ERR

exemplo comum Muitos guias sugerem usar algo como:

trap 'echo "Error en línea $LINENO" >&2' ERR linha de captura

Registrar falhas com o número da sua linha. Isso é bom, mas é recomendável. Entenda seus limitesArmadilha pode:

  • Executar código adicional quando um erro (ERR) for acionado.Por exemplo, escrever em um registro com data, script e linha.
  • Acesse o código de saída do último comando com $?.
  • saber a linha de script com $LINENOMuito útil para depuração.
  Como criar e gerenciar perfis automáticos no Gigabyte Control Center

Retirei não pode fazer O tratamento de erros (ERR) é usado para "resgatar" a saída de erro padrão (stderr) de um comando que já foi executado, a menos que você a tenha redirecionado previamente para um arquivo ou descritor que possa ser lido. O erro em si é um fluxo de texto, não um valor único.

Alguns padrões envolvem o redirecionamento do stderr de todo o script para um log temporário e, na armadilha, Adicione um cabeçalho com a data, o arquivo e a linha. Isso permite correlacionar esse bloco de erros com o ponto do script onde a falha ocorreu. Dá mais trabalho, mas quando algo falha em produção, ter contexto é fundamental.

Registro de logs em Bash: gravando apenas o necessário, formatado e sem poluir o código.

Se você automatizar até mesmo tarefas minimamente críticas, precisa saber O que aconteceu, quando e com que resultado?É por isso que quase todos os exemplos de scripts sérios acabam incluindo uma função de registro centralizada que unifica o formato.

função típica Uma função típica poderia ser assim:

log_message() {
local level="$1"
local message="$2"
local timestamp=$(date "+%Y-%m-%d %H:%M:%S")
echo -e "${timestamp} ${message}" | tee -a "$LOG_FILE"
}
formato unificado

exemplos Com algo assim você pode chamar:

  • mensagem_de_log "INFO" "Iniciando verificação da CPU"
  • mensagem_de_log "AVISO" "Uso de disco acima de 85%"
  • mensagem_de_log "ERRO" "Não foi possível conectar ao banco de dados"

e utilize sempre o mesmo formato, com a data e o nível claramente indicados. camiseta -a Também permite visualizar a mensagem na tela e salvá-la em um arquivo de registro simultaneamente, o que é muito conveniente tanto para uso interativo quanto para análise posterior.

Outra boa prática é separar registros de atividades (o que o roteiro faz) e métricas ou registros de resultados (Valores de CPU, RAM, disco, etc.) em arquivos diferentes. Por exemplo:

  • /var/log/system-monitor.log Para eventos e mensagens.
  • /var/log/system-metrics.log para registros periódicos de status.

facilita a integração Isso facilita o fornecimento de dados a ferramentas externas, analisadores sintáticos ou até mesmo soluções de observabilidade, sem misturar mensagens humanas com dados de máquina.

Verificação do estado do sistema: CPU, memória, disco, rede e registros.

Um uso muito comum do Bash em ambientes Linux é implementar um script de "verificação rápida do sistema" que coleta todas as informações necessárias de uma só vez. Informações básicas para diagnosticar problemasEsse tipo de script geralmente inclui vários blocos:

  • carga da CPU: vestindo mpstat se estiver disponível (graças ao pacote sysstat), ou recorrendo a top Em modo batch, para obter um resumo do uso da CPU.
  • Principais processos por CPU: com ps aux --sort=-%cpu | head -n N Para ver o que está consumindo mais recursos.
  • Espaço em disco: com df -h, prestando especial atenção à partição raiz e a quaisquer volumes críticos para a aplicação ou bases de dados (veja como liberar espaço no Linux).
  • Memória RAM e SWAP: através de free -hPara verificar quanto está sendo usado e se há uso excessivo de swap.
  • Status da rede: verificar a conectividade externa com um ping para um IP conhecido (normalmente 8.8.8.8) e listar as interfaces ativas com ip addr showe utilizando ferramentas como netcat (nc/ncat) para testes mais avançados.
  • Registros recentes do sistema: desenhando as últimas linhas com journalctl -n ou equivalente, para detectar erros de serviço imediatamente antes da execução do script.

O resultado geralmente é salvo em um arquivo de log ou exibido em um formato formatado na tela, servindo como Visão geral rápida da saúde do sistema que você pode revisar por conta própria ou integrar em ferramentas de monitoramento.

Instalação automática de dependências e verificação de privilégios

pré-condições Um detalhe importante de muitos scripts de administração é que, antes de executar qualquer ação séria, eles verificam se duas condições são atendidas:

  • O script está sendo executado como raiz (ou via sul).
  • As ferramentas necessárias estão instaladas. dependendo do sistema operacional.

verificação raiz A verificação de root geralmente é feita com $EUID:

check_root() {
if ; then
log_message "ERROR" "Este script necesita privilegios de root para instalar paquetes."
log_message "WARNING" "Por favor, ejecute con sudo."
exit 1
fi
}
O processo falha se você não tiver acesso root.

Dessa forma, você evita erros subsequentes devido a permissões insuficientes e envia uma mensagem clara para o usuário desde o início.

  Como ativar alertas de flash LED no iPhone

detectar distribuição A instalação de dependências geralmente é suportada por /etc/os-release Para detectar a família de distribuição:

  • En Debian / Ubuntu veja um apt-get para instalar pacotes como sysstat, lm-sensors, net-tools.
  • En CentOS / RHEL / Fedora seria puxado dnf o yum com pacotes equivalentes (lm_sensors, net-tools, etc.).

Alertas, cron e exportação de logs: levando o script para produção.

Depois de ter um script de verificação ou automação razoavelmente robusto, o próximo passo lógico é Integre-o à vida diária.:

  • Alertas por e-mail, Slack ou outros canais.Por exemplo, se o uso do disco ultrapassar 90%, você envia um e-mail usando o mailx ou um webhook do Slack. O padrão típico é verificar o valor com df/awk/sed e, se ele ultrapassar um determinado limite, acionar a notificação.
  • Execução periódica com cron: adicione uma entrada como esta 0 8 * * * /ruta/a/script.sh para que funcione todos os dias em um horário específico.
  • Redirecionando a saída para arquivos de log.: executar ./script.sh > /var/log/system-check.log 2>&1 Se você deseja registrar todas as saídas padrão e de erro em um único arquivo.

É importante entender que, se o seu roteiro for projetado para Pare ao primeiro erro. Graças a `set -euo pipefail`, isso significa que o cron ou o pipeline de CI/CD que o chama verá claramente um código de saída diferente de zero Quando ocorre uma falha, permite acionar fluxos de recuperação, marcar compilações como falhas, etc.

Depuração em Bash: defina -x, -v, -ny para rastrear o arquivo

O Bash não possui um depurador como algumas outras linguagens, mas oferece diversas opções. opções de depuração muito poderosas que, quando usadas corretamente, economizam muitas horas de "por que diabos isso está acontecendo?":

  • definir -x o definir -o xtraceExibe cada comando executado com todos os seus parâmetros já expandidos. Ideal para visualizar os valores reais que as variáveis ​​estão assumindo.
  • definir -v o definir -o verboseImprime as linhas do script à medida que são lidas, mesmo antes da expansão. Útil para entender o fluxo.
  • definir -n o set -o noexecAnalisa a sintaxe sem executar o código, perfeito para detectar aspas, chaves ou colchetes ausentes.
  • definir -uComo já mencionado, ele falha quando variáveis ​​indefinidas são usadas, o que é muito útil durante a depuração de erros lógicos.

Além disso, você pode limitar o efeito dessas opções a um fragmento específico do roteiro Por exemplo, se estiver usando `set -x` ou `set +x`:

set -x
# bloque problemático
read -p "Pass Dir name : " D_OBJECT
read -p "Pass File name : " F_OBJECT
set +x
registro temporal

#!/bin/bash
exec 6> salida_debug.log
BASH_XTRACEFD="6"
set -x
# ... resto del script ...
redirecionar rastreamento

Assim, todas as informações de depuração são armazenadas em debug_output.logE você poderá continuar vendo apenas a saída "normal" do script no terminal.

Exemplo típico de um bug com variáveis ​​indefinidas

Um problema muito comum quando você não usa `set -u` é invocar variáveis ​​que não existem Sem que você perceba. Imagine um script que solicita o nome de um objeto e verifica se é um arquivo ou diretório, mas erra o nome da variável:

#!/bin/bash
read -p "Nombre del Objeto : " OBJECT
if ]; then
echo "$OBJECT es un archivo"
elif ]; then
echo "$OBJECT es un directorio"
fi
erro variável

Como $OBJECT1 não está definidoO Bash expande para uma string vazia, os testes -fy -d não geram erro, simplesmente não são atendidos, e o script termina com o código 0. Nada "acontece", mas logicamente o resultado não é o esperado.

Se, em vez disso, você executar o script com bash -u nome_do_script ou ativo definir -uVocê receberá imediatamente um erro de "Variável não vinculada", o que leva diretamente a uma falha de tipo. Combinado com bash -x nome_do_script Ou, usando `set -x`, você também verá qual linha e quais valores específicos estão sendo usados, facilitando muito a depuração.

Criando contêineres leves com Podman no Linux
Artigo relacionado:
Contêineres leves com Podman no Linux: um guia prático