Tratamento de erros em scripts: set -e, set -u, set -o pipefail
1. Introdução ao tratamento de erros em Bash
O comportamento padrão do Bash é notoriamente permissivo quando se trata de erros. Por padrão, um script continua executando mesmo quando um comando falha, ignorando silenciosamente códigos de saída diferentes de zero. Isso significa que um erro em uma etapa intermediária pode passar despercebido, corrompendo dados, gerando saídas incompletas ou causando falhas catastróficas em momentos inesperados.
Considere um script de backup que tenta copiar arquivos, compactá-los e enviá-los para um servidor remoto. Se a compactação falhar, o script continua e envia um arquivo corrompido. Sem tratamento de erros, essas falhas se propagam silenciosamente.
As opções set -e, set -u e set -o pipefail oferecem um controle granular sobre como o Bash reage a erros, transformando scripts frágeis em programas robustos e previsíveis.
2. set -e: Abortando na primeira falha
A opção set -e instrui o Bash a interromper imediatamente a execução do script quando qualquer comando retornar um código de saída diferente de zero. Isso evita que erros se propaguem.
#!/bin/bash
set -e
echo "Criando diretório..."
mkdir /tmp/meu_diretorio
echo "Copiando arquivos..."
cp /etc/passwd /tmp/meu_diretorio/
echo "Removendo diretório..."
rm -rf /tmp/meu_diretorio_inexistente # Isso interrompe o script
echo "Esta linha nunca será executada"
Neste exemplo, rm -rf em um diretório inexistente retorna código 1, fazendo o script abortar antes da última mensagem.
Exceções importantes: set -e não afeta comandos dentro de condicionais (if, while, until) ou em listas &&/||. Isso permite usar comandos que podem falhar como condições lógicas:
#!/bin/bash
set -e
if grep -q "root" /etc/passwd; then
echo "Usuário root encontrado"
fi
# O comando grep acima pode falhar sem interromper o script
Armadilhas comuns: Comandos como grep que não encontram padrões retornam código 1. Em set -e, isso interrompe o script se não estiver dentro de um condicional. Da mesma forma, rm em arquivo inexistente ou cd em diretório que não existe são causas frequentes de abortos inesperados.
3. set -u: Detectando variáveis não definidas
Variáveis não definidas são uma fonte comum de bugs em scripts Bash. Por padrão, o Bash expande variáveis não definidas para strings vazias, mascarando erros de digitação ou lógica.
#!/bin/bash
set -u
nome="João"
echo "Olá, $nome" # Funciona
echo "Olá, $noma" # Erro: variável não definida, script aborta
Com set -u, qualquer tentativa de expandir uma variável não definida resulta em erro fatal. Isso é particularmente útil para detectar:
- Erros de digitação em nomes de variáveis
- Parâmetros de função não fornecidos
- Variáveis de ambiente ausentes
Cuidados com $* e $@: Em scripts que recebem argumentos, set -u pode causar erros se você tentar acessar $1 sem verificar se o argumento foi fornecido. A solução é usar ${1:-valor_default} para fornecer valores padrão.
Interação com arrays: Arrays vazios também são afetados. Se você declarar um array e tentar acessar seu primeiro elemento sem verificá-lo, set -u pode interromper o script.
4. set -o pipefail: Falhas em pipelines
O comportamento padrão do Bash em pipelines é considerar apenas o código de saída do último comando. Isso significa que um erro no meio do pipeline pode passar despercebido:
#!/bin/bash
# Comportamento padrão
comando_que_falha | comando_ok
echo $? # Mostra 0 (sucesso), mesmo se o primeiro comando falhou
set -o pipefail altera esse comportamento: o pipeline retorna o código de saída do último comando que falhou (ou zero se todos tiverem sucesso).
#!/bin/bash
set -o pipefail
cat /etc/arquivo_inexistente | grep "root" | sort
echo "Código de saída: $?" # Mostra o erro do cat, não do sort
Exemplo prático com pipeline de três estágios:
#!/bin/bash
set -o pipefail
# Simulação: comando2 falha
comando1() { echo "dado1"; }
comando2() { return 1; }
comando3() { cat; }
comando1 | comando2 | comando3
echo "Pipeline falhou com código: $?" # Mostra 1 (erro do comando2)
5. Combinando set -e, set -u e set -o pipefail
A combinação das três opções forma a tríade de segurança para scripts Bash modernos. O idiom set -euo pipefail é amplamente adotado como padrão em scripts profissionais.
#!/bin/bash
set -euo pipefail
# Script robusto de processamento de dados
DIR_DADOS="/tmp/dados"
ARQUIVO_SAIDA="/tmp/resultado.txt"
echo "Verificando diretório de dados..."
[ -d "$DIR_DADOS" ] || { echo "Erro: diretório $DIR_DADOS não existe"; exit 1; }
echo "Processando arquivos..."
for arquivo in "$DIR_DADOS"/*.txt; do
[ -f "$arquivo" ] || continue # Proteção contra glob sem match
echo "Processando: $arquivo"
cat "$arquivo" | grep -v "^#" | sort -u >> "$ARQUIVO_SAIDA"
done
echo "Compactando resultado..."
gzip "$ARQUIVO_SAIDA"
echo "Script concluído com sucesso"
Ordem de avaliação: As opções são aplicadas na ordem em que são encontradas, mas é recomendável declará-las juntas no início do script. A combinação euo funciona porque:
- -e interrompe em qualquer falha
- -u detecta variáveis não definidas
- -o pipefail garante que falhas em pipelines sejam propagadas
6. Limitações e boas práticas complementares
Limitações do set -e:
- Subshells: (comando_que_falha) dentro de parênteses não interrompe o script pai
- eval: Comandos avaliados dinamicamente podem escapar do controle
- Comandos built-in: source com arquivo inexistente não é capturado
Uso de trap com ERR: Para ações personalizadas em falhas:
#!/bin/bash
set -euo pipefail
trap 'echo "Erro na linha $LINENO"; exit 1' ERR
echo "Executando tarefa perigosa..."
rm -rf /diretorio_importante # Se falhar, trap é acionado
Desabilitando temporariamente: Use set +e e set +u para seções específicas:
#!/bin/bash
set -euo pipefail
# Seção onde falhas são aceitáveis
set +e
grep "padrao" /arquivo/opcional || true
set -e
# Script continua normalmente
7. Debugging com set -x e validação final
set -x (ou set -o xtrace) é diferente das opções de tratamento de erros. Enquanto -e, -u e -o pipefail controlam quando o script para, -x controla o que é exibido durante a execução.
#!/bin/bash
set -euo pipefail
set -x # Ativa modo de depuração
echo "Depurando script..."
variavel="teste"
echo "$variavel"
set +x # Desativa depuração
echo "Modo normal"
Checklist para validar robustez de scripts:
set -euo pipefailestá no início do script?- Comandos que podem falhar legitimamente estão dentro de condicionais?
- Variáveis têm valores padrão quando necessário (
${var:-default})? trap ERRestá configurado para logging ou ações de limpeza?- O script foi testado com entradas inválidas e situações de erro?
- Há verificações explícitas para arquivos e diretórios antes de usá-los?
set +eé usado apenas em blocos específicos e com|| truequando apropriado?
A combinação set -euo pipefail não é uma bala de prata, mas estabelece uma base sólida para scripts confiáveis. Combinada com boas práticas de validação e tratamento de erros, ela transforma scripts Bash de utilitários frágeis em ferramentas robustas adequadas para automação de produção.
Referências
- GNU Bash Manual: The Set Builtin — Documentação oficial das opções
setincluindo-e,-ue-o pipefail - Bash Hackers Wiki: The set command — Guia detalhado sobre todas as opções do comando
setcom exemplos práticos - Google Shell Style Guide: Error Handling — Diretrizes do Google para tratamento de erros em scripts shell, incluindo
set -euo pipefail - ShellCheck: SC2251 - set -euo pipefail — Ferramenta de análise estática que recomenda e explica o uso de
set -euo pipefail - Red Hat Developer: Writing robust Bash scripts — Artigo técnico sobre como escrever scripts Bash robustos com tratamento de erros