Trap: executando código ao sair do script
1. Introdução ao comando trap
O comando trap é uma das ferramentas mais poderosas do Bash para controle de fluxo e tratamento de eventos. Ele permite que você execute código específico quando um sinal é recebido pelo shell, seja durante a execução normal do script ou quando algo inesperado acontece.
A sintaxe básica é simples:
trap 'comando' SINAL
Onde 'comando' é o código a ser executado e SINAL é o nome do sinal a ser capturado.
Os sinais mais comuns no contexto de scripts Bash são:
EXIT— executado quando o script termina (por qualquer motivo)INT(SIGINT) — enviado quando o usuário pressiona Ctrl+CTERM(SIGTERM) — sinal de término enviado por processos externosERR— executado quando um comando retorna código de erro diferente de zero
2. Capturando a saída normal do script com trap EXIT
O sinal EXIT é especial no Bash: ele é acionado sempre que o script termina, independentemente de ter sido bem-sucedido ou não. Isso o torna ideal para tarefas de limpeza.
#!/bin/bash
tmpfile=$(mktemp)
trap 'echo "Limpando arquivo temporário..."; rm -f "$tmpfile"' EXIT
echo "Escrevendo dados no arquivo temporário..."
echo "dados importantes" > "$tmpfile"
# Simula algum processamento
sleep 2
echo "Script finalizado com sucesso"
A diferença crucial entre trap ... EXIT e código colocado após o corpo principal do script é que o trap será executado mesmo se o script for interrompido abruptamente ou se houver um exit explícito em qualquer ponto.
#!/bin/bash
cleanup() {
echo "Executando limpeza..."
}
trap cleanup EXIT
if [ "$1" = "erro" ]; then
echo "Erro detectado, saindo..."
exit 1 # O trap será executado aqui
fi
echo "Script executado normalmente"
3. Tratando interrupções do usuário: trap INT e trap TERM
Interrupções do usuário são comuns em scripts longos. O trap INT captura Ctrl+C, permitindo um desligamento gracioso.
#!/bin/bash
salvar_estado() {
echo "Salvando estado antes de sair..."
# Salvar progresso em arquivo de checkpoint
echo "$contador" > /tmp/checkpoint.txt
}
trap 'salvar_estado; exit 1' INT
contador=0
while true; do
((contador++))
echo "Processando item $contador..."
sleep 1
done
Para ignorar completamente interrupções, use uma string vazia como comando:
#!/bin/bash
trap '' INT # Ignora Ctrl+C
echo "Este script não pode ser interrompido com Ctrl+C"
sleep 10
echo "Concluído"
O sinal TERM é útil para scripts que precisam responder a sinais de término enviados por outros processos (como o comando kill):
#!/bin/bash
trap 'echo "Recebido SIGTERM, salvando dados..."; exit 0' TERM
echo "PID do processo: $$"
echo "Envie 'kill $$' para testar"
while true; do
echo "Trabalhando..."
sleep 2
done
4. Usando trap ERR para depuração e rollback
O sinal ERR é acionado sempre que um comando retorna um código de erro. Combinado com set -e, ele cria um mecanismo poderoso para tratamento de erros.
#!/bin/bash
set -e # Sai do script ao primeiro erro
rollback() {
echo "Erro detectado! Realizando rollback..."
# Reverter alterações no banco de dados
echo "DELETE FROM transacoes WHERE status='pendente';" | mysql
}
trap 'rollback' ERR
echo "Iniciando transação..."
mysql -e "INSERT INTO transacoes (status) VALUES ('pendente')"
# Comando que pode falhar
processar_dados() {
return 1 # Simula uma falha
}
processar_dados
echo "Transação concluída com sucesso" # Não será executado
5. Múltiplos traps e ordem de execução
Quando você define múltiplos traps para o mesmo sinal, o último definido substitui o anterior. Para acumular comportamentos, use funções:
#!/bin/bash
trap1() { echo "Primeira tarefa de limpeza"; }
trap2() { echo "Segunda tarefa de limpeza"; }
cleanup() {
trap1
trap2
}
trap cleanup EXIT
echo "Script em execução..."
A ordem de execução quando múltiplos sinais são recebidos segue a prioridade dos sinais. Geralmente, EXIT é executado por último, após os handlers de INT ou TERM.
6. Limpando traps e restaurando comportamento padrão
Para remover um trap e restaurar o comportamento padrão de um sinal, use trap - SINAL:
#!/bin/bash
trap 'echo "Interrompido!"' INT
echo "Trap ativo para Ctrl+C"
sleep 3
trap - INT # Restaura comportamento padrão
echo "Trap removido - Ctrl+C agora funciona normalmente"
sleep 3
echo "Script concluído"
7. Casos de uso avançados
Trap em subshells
Traps definidos em subshells não afetam o shell pai:
#!/bin/bash
trap 'echo "Trap do shell pai"' EXIT
(
trap 'echo "Trap do subshell"' EXIT
echo "Dentro do subshell"
)
echo "De volta ao shell pai"
Usando trap DEBUG para rastreamento
O sinal DEBUG é executado antes de cada comando, útil para debugging:
#!/bin/bash
trap 'echo "Executando: $BASH_COMMAND"' DEBUG
echo "Primeiro comando"
ls /tmp
echo "Último comando"
Watchdog com trap ALRM
#!/bin/bash
TIMEOUT=5
watchdog() {
echo "Timeout! Processo excedeu $TIMEOUT segundos"
exit 1
}
trap watchdog ALRM
echo "Iniciando processo com timeout de $TIMEOUT segundos..."
sleep 10 &
pid=$!
(sleep $TIMEOUT; kill -ALRM $$) &
sleep_pid=$!
wait $pid
kill $sleep_pid 2>/dev/null
echo "Processo concluído dentro do tempo"
8. Boas práticas e armadilhas comuns
Aspas corretas: Use aspas simples para evitar expansão de variáveis no momento da definição do trap:
# Correto - variável será expandida quando o trap for executado
trap 'echo "Arquivo: $file"' EXIT
# Incorreto - variável é expandida imediatamente
trap "echo \"Arquivo: $file\"" EXIT
Evite traps dentro de loops: Cada iteração pode redefinir o trap, causando comportamentos inesperados:
# Evite isso
for i in {1..5}; do
trap "echo 'Loop $i interrompido'" INT
sleep 1
done
Testando traps: Use kill -SINAL $$ para testar traps durante o desenvolvimento:
#!/bin/bash
trap 'echo "Teste de sinal bem-sucedido"' USR1
echo "Testando com kill -USR1 $$"
kill -USR1 $$
Sempre limpe recursos: Um trap EXIT bem definido deve garantir que arquivos temporários, descritores de arquivo e outros recursos sejam liberados, independentemente de como o script termina.
Referências
-
GNU Bash Manual: Trap — Documentação oficial do Bash sobre o comando trap, incluindo todos os sinais suportados e sintaxe detalhada.
-
Advanced Bash-Scripting Guide: Traps — Guia avançado com exemplos práticos e explicações detalhadas sobre o uso de traps em scripts complexos.
-
Bash Hackers Wiki: Trap — Wiki colaborativa com exemplos avançados, incluindo traps em subshells e tratamento de múltiplos sinais.
-
Linux man page: signal(7) — Página de manual completa sobre sinais no Linux, com lista completa de sinais e seus comportamentos padrão.
-
Shell Scripting Tutorial: Trap Command — Tutorial prático com exemplos de uso de traps para limpeza de recursos e tratamento de erros em scripts.