trap - Manipulacao de Sinais e Limpeza no Bash
O Que Voce Vai Aprender
- Como usar
trappara capturar sinais e garantir que a limpeza seja executada - Como prevenir o bug classico onde Ctrl-C deixa arquivos temporarios para tras
- Um template pronto para producao combinando o pseudo-sinal
EXIT, uma funcaocleanupeset -e
Resumo Rapido
- Canalize toda limpeza em um unico
trap cleanup EXIT(ele roda nao importa como o script termine) - Crie arquivos temporarios com
mktemp, depois defina otrapimediatamente SIGKILL(9) eSIGSTOPnao podem ser capturados. Trate tudo o mais.
Pre-requisitos
- Shell: bash (
EXIT/INT/TERMtambem funcionam em POSIX sh) - Publico: usuarios intermediarios escrevendo scripts shell
O Que E o trap?
Conclusao: trap e um builtin do bash que registra um comando para executar quando um sinal ou evento especial ocorre. E como voce agenda limpeza.
trap registra um manipulador de interrupcao: "quando este sinal chegar, execute este comando." A sintaxe basica e:
trap 'comando' SINAL...
Por exemplo, capturando Ctrl-C (SIGINT) para imprimir uma mensagem:
trap 'echo "Interrupted"' INT
Um sinal pode ser nomeado SIGINT, INT, ou dado por numero 2. O prefixo SIG e opcional, e para portabilidade o nome simples (INT / TERM / EXIT) e preferido.
O primeiro argumento e uma string que o shell avalia. Aspas simples adiam a expansao ate o sinal realmente chegar. Aspas duplas expandem variaveis quando o trap e definido, entao seja deliberado sobre aspas ao embutir variaveis de limpeza.
Por Que Usar o Pseudo-sinal EXIT?
Conclusao: EXIT e um pseudo-sinal que dispara no instante em que um script termina, seja em sucesso, erro ou Ctrl-C. Canalizar a limpeza aqui e a abordagem mais robusta.
Capturar sinais reais (INT / TERM) um por um arrisca perder um e pular a limpeza. EXIT reage ao evento "script esta terminando" em vez de um sinal, entao ele roda exatamente uma vez independentemente de como o script termine.
#!/usr/bin/env bash tmpfile=$(mktemp) trap 'rm -f "$tmpfile"' EXIT # O que quer que aconteca depois (saida normal, falha com set -e, Ctrl-C), # tmpfile e sempre removido quando o script termina. echo "working..." > "$tmpfile" cat "$tmpfile"
O trap EXIT nao roda com SIGKILL (kill -9). SIGKILL encerra o processo imediatamente, sem dar chance de executar um manipulador. Entenda isso como um limite rigido do trap.
Como Voce Escreve uma Funcao de Cleanup?
Conclusao: Colete a limpeza em uma funcao
cleanupe registre-a comtrap cleanup EXIT. Mesmo com muitos recursos temporarios, voce gerencia a desmontagem em um unico lugar.
Uma string de comando inline e suficiente para trabalho curto, mas a legibilidade sofre conforme a lista de coisas para remover cresce. Extrair uma funcao e a pratica padrao.
#!/usr/bin/env bash
set -euo pipefail
workdir=$(mktemp -d)
lockfile="/tmp/myjob.lock"
cleanup() {
local rc=$? # salvar o codigo de saida anterior
rm -rf "$workdir"
rm -f "$lockfile"
echo "cleanup done (exit=$rc)"
exit "$rc" # sair com o codigo original
}
trap cleanup EXIT
# --- trabalho principal ---
touch "$lockfile"
echo "data" > "$workdir/output.txt"A linha chave e local rc=$? no topo da funcao. Executar outros comandos dentro de cleanup sobrescreve $?, entao voce salva o codigo de saida primeiro e o restaura com exit "$rc". Isso significa "limpe, mas ainda reporte o erro real para quem chamou."
Criar um diretorio de trabalho com mktemp -d e remove-lo com rm -rf "$workdir" e mais seguro do que rastrear arquivos temporarios individuais. Um workdir vazio tornaria rm -rf perigoso, entao set -u (erro em variaveis indefinidas) previne esse problema.
Como Voce Trata Multiplos Sinais?
Conclusao: Canalize a limpeza em EXIT, e so capture INT / TERM individualmente quando precisar de comportamento de interrupcao personalizado. Alguns sinais nao podem ser capturados.
Aqui estao os principais sinais e seus usos.
| Sinal | Numero | Disparado por | Capturavel |
|---|---|---|---|
INT |
2 | Ctrl-C | Sim |
TERM |
15 | kill padrao |
Sim |
HUP |
1 | Desconexao do terminal | Sim |
QUIT |
3 | Ctrl-\ | Sim |
EXIT |
0 | Script termina (pseudo) | Sim |
KILL |
9 | kill -9 |
Nao |
STOP |
19 | Suspensao de processo | Nao |
Imprima uma mensagem personalizada em INT enquanto deixa a desmontagem para EXIT:
#!/usr/bin/env bash tmpfile=$(mktemp) trap 'rm -f "$tmpfile"' EXIT trap 'echo "Interrupted by user"; exit 130' INT echo "Running... press Ctrl-C to interrupt" sleep 30
Quando o manipulador INT encerra o script com exit 130, o trap EXIT entao dispara e remove tmpfile. Por convencao, o codigo de saida para INT e 128 + 2 = 130.
SIGKILL (9) e SIGSTOP (19) nao podem ser capturados, ignorados ou redefinidos por design do SO. O requisito "limpar mesmo com kill -9" e impossivel com trap. Trate isso de fora, ex. um processo supervisor ou ExecStopPost= do systemd.
Como Voce Combina com set -e de Forma Segura?
Conclusao: set -e (sair em erro) combina bem com trap. Mesmo se um comando falhar no meio, o trap executa a limpeza, sem deixar estado incompleto.
Com set -e, o script sai no momento em que um comando falha. O trap EXIT ainda roda, entao a limpeza e garantida. Adicionar o pseudo-sinal ERR (uma extensao do bash) permite registrar qual comando falhou.
#!/usr/bin/env bash set -euo pipefail workdir=$(mktemp -d) trap 'rm -rf "$workdir"' EXIT trap 'echo "error: failed at line $LINENO (exit=$?)" >&2' ERR cp /etc/hostname "$workdir/" grep "nonexistent string" "$workdir/hostname" # falha aqui -> ERR e EXIT disparam echo "never reached"
O trap ERR reporta a falha e o trap EXIT limpa - um padrao de dois estagios que e a base de um script defensivo.
Adicionar set -o pipefail tambem detecta falhas no meio de um pipeline. set -euo pipefail + trap cleanup EXIT vale memorizar como o template de fortalecimento para scripts shell.
Como Voce Inspeciona ou Remove um Trap?
Conclusao: Liste traps ativos com trap -p, e remova um com trap - SINAL. Ambos sao uteis ao debugar.
Liste os traps atualmente definidos com trap -p.
$ trap 'rm -f /tmp/x' EXIT $ trap -p trap -- 'rm -f /tmp/x' EXIT
Para remover um trap para um sinal especifico, defina seu comando como -.
trap - EXIT # remover o trap EXIT (restaurar comportamento padrao)
Para ignorar um sinal (nao fazer nada quando ele chegar), defina o comando como uma string vazia.
trap '' INT # desabilitar Ctrl-C
Definir trap '' INT para ignorar e herdado pelos processos filhos. Se voce so quer desabilitar Ctrl-C em parte de um script, restaure com trap - INT ao sair da secao critica.
Resumo: o Template Seguro de trap
Conclusao: Crie recursos temporarios com mktemp, defina trap cleanup EXIT imediatamente, e faca o cleanup salvar o codigo de saida antes de desmontar. Este e o padrao que nao falha.
Copiar e colar: esqueleto de um script robusto
#!/usr/bin/env bash
set -euo pipefail
workdir=$(mktemp -d)
cleanup() {
local rc=$?
rm -rf "$workdir"
exit "$rc"
}
trap cleanup EXIT
trap 'echo "interrupted" >&2; exit 130' INT
# --- trabalho principal vai aqui ---
echo "scratch" > "$workdir/tmp.txt"Tres pontos para lembrar:
- Canalize a limpeza em
EXIT(ele roda em sucesso, falha e interrupcao) - Salve o codigo de saida no topo de
cleanupcomlocal rc=$?, depoisexit "$rc" SIGKILL/SIGSTOPnao podem ser capturados. Projete com isso em mente.
Como proximo passo, tente escrever um script pratico baseado em trap (gerenciamento de lock-file, um job agendado) junto com Basico do getopts ou Basico do Cron para aprofundar seu entendimento.