Manipuladores trap para limpeza e sinais
Registre traps para EXIT, ERR e INT a fim de remover arquivos temporários e reverter trabalhos parciais de forma confiável.
Manipuladores trap para limpeza e sinais é uma aula grátis de DevOps Bootcamp no CoddyKit. Esta é a aula 2 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de DevOps Bootcamp, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de DevOps Bootcamp inclui 4 aulas no total.
Por que os manipuladores de armadilhas são importantes
Quando um script Bash é encerrado — seja normalmente, devido a um erro ou porque o usuário pressionou Ctrl+C — ele pode deixar arquivos temporários, dados parcialmente gravados ou um estado inconsistente. Sem limpeza, esses artefatos se acumulam e causam problemas difíceis de depurar.
Os manipuladores de armadilhas resolvem isso registrando uma função ou um comando que o Bash executa automaticamente quando recebe um sinal específico ou um pseudossinal.
- EXIT — é acionado sempre que o script é encerrado, por qualquer motivo
- ERR — é acionado depois que qualquer comando retorna um status de saída diferente de zero
- INT — é acionado quando o usuário envia SIGINT (Ctrl+C)
- TERM — é acionado quando SIGTERM é enviado (por exemplo, por
kill)
Um script bem escrito registra essas armadilhas logo no início, antes que qualquer operação arriscada comece.
A sintaxe do comando integrado trap
O comando integrado trap registra um manipulador para um ou mais sinais ou pseudossinais. A sintaxe geral é:
trap 'command_or_function' SIGNAL [SIGNAL...]
Regras principais:
- O primeiro argumento é uma cadeia entre aspas contendo código do shell (ou o nome de uma função) a ser executado quando o sinal for acionado.
- Você pode listar vários sinais depois do manipulador.
trap '' SIGNALignora esse sinal (manipulador vazio).trap - SIGNALredefine o sinal para seu comportamento padrão.
As armadilhas são herdadas pelas funções chamadas no mesmo shell, mas não pelos subshells iniciados com ( ) ou &.
#!/usr/bin/env bash
# Syntax examples — not a full script
# Register a cleanup function on EXIT
trap cleanup EXIT
# Inline handler for INT and TERM
trap 'echo "Interrupted!"; exit 1' INT TERM
# Ignore SIGHUP
trap '' HUP
# Reset SIGPIPE to default
trap - PIPEA armadilha EXIT — sua rede de segurança
O pseudossinal EXIT é a armadilha mais importante a ser registrada. Ele é acionado quando o shell é encerrado por qualquer motivo: conclusão normal, exit N, erro não tratado ou sinal. Isso o torna ideal para remover arquivos temporários.
Prática recomendada:
- Crie arquivos temporários com
mktemppara que o nome seja único e imprevisível. - Armazene imediatamente o caminho em uma variável.
- Registre a armadilha EXIT logo após a criação, para que a limpeza sempre seja executada.
O código de saída do script é preservado — o código de saída do manipulador da armadilha é ignorado, a menos que você chame explicitamente exit dentro dele.
#!/usr/bin/env bash
set -euo pipefail
TMPFILE=$(mktemp /tmp/myapp.XXXXXX)
cleanup() {
echo "Removing temp file: $TMPFILE" >&2
rm -f "$TMPFILE"
}
trap cleanup EXIT
# Do real work
echo "Processing..." > "$TMPFILE"
sort "$TMPFILE" -o "$TMPFILE"
cp "$TMPFILE" /tmp/myapp_result.txt
echo "Done. Result saved."Como lidar com vários arquivos e diretórios temporários
Scripts reais geralmente criam vários artefatos temporários — arquivos, diretórios, FIFOs e arquivos de bloqueio. Em vez de acompanhar cada um separadamente, reúna-os em uma matriz e remova tudo em uma única função de limpeza.
Esse padrão é fácil de ampliar: sempre que criar um novo recurso temporário, adicione-o à matriz. A armadilha EXIT sempre lida com a lista completa, independentemente de onde o script seja encerrado.
#!/usr/bin/env bash
set -euo pipefail
CLEANUP_TARGETS=()
add_cleanup() {
CLEANUP_TARGETS+=("$1")
}
cleanup() {
echo "Running cleanup..." >&2
for target in "${CLEANUP_TARGETS[@]:-}"; do
rm -rf "$target"
echo "Removed: $target" >&2
done
}
trap cleanup EXIT
# Create and track temp resources
TMPDIR_WORK=$(mktemp -d /tmp/work.XXXXXX)
add_cleanup "$TMPDIR_WORK"
TMPFILE_LOG=$(mktemp /tmp/run.XXXXXX.log)
add_cleanup "$TMPFILE_LOG"
echo "Working in $TMPDIR_WORK" | tee "$TMPFILE_LOG"
touch "$TMPDIR_WORK/output.txt"
echo "Step complete."A armadilha ERR — capturando falhas
O pseudossinal ERR é acionado depois que qualquer comando simples retorna um status de saída diferente de zero, desde que a opção do shell set -e (errexit) esteja ativa ou que o comando não esteja dentro de uma construção composta if, while ou ||/&&.
Use a armadilha ERR para:
- Registrar qual linha falhou usando
$LINENOe$BASH_COMMAND. - Acionar a lógica de reversão antes da execução da armadilha EXIT.
- Enviar mensagens de erro estruturadas para stderr.
Importante: as armadilhas ERR não são acionadas dentro de funções, a menos que você execute explicitamente set -E (errtrace), para que as funções herdem a armadilha ERR do shell chamador.
#!/usr/bin/env bash
set -eEuo pipefail
on_error() {
local exit_code=$?
local line=$1
echo "ERROR: command failed with exit code $exit_code at line $line" >&2
echo " Command: $BASH_COMMAND" >&2
}
trap 'on_error $LINENO' ERR
echo "Starting task..."
cp /nonexistent/file /tmp/ # This will fail
echo "This line is never reached."A armadilha INT — Ctrl+C tratado com segurança
Pressionar Ctrl+C envia SIGINT ao grupo de processos em primeiro plano. Sem uma armadilha, o Bash é encerrado imediatamente, possivelmente deixando trabalho parcial para trás.
Registrar uma armadilha INT permite que você:
- Exiba uma mensagem de cancelamento amigável.
- Execute qualquer reversão intermediária antes de passar o controle para a armadilha EXIT.
- Encerre com um código de status significativo (
130é o código convencional para SIGINT:128 + 2).
Depois que o manipulador INT terminar, chame exit 130 para propagar o código de saída correto. A armadilha EXIT será executada automaticamente em seguida.
#!/usr/bin/env bash
set -euo pipefail
TMPFILE=$(mktemp /tmp/demo.XXXXXX)
cleanup() {
rm -f "$TMPFILE"
echo "Temp file removed." >&2
}
on_interrupt() {
echo "" >&2
echo "Caught SIGINT — cancelling gracefully." >&2
exit 130 # EXIT trap (cleanup) runs automatically after this
}
trap cleanup EXIT
trap on_interrupt INT
echo "Running long task. Press Ctrl+C to cancel."
for i in $(seq 1 10); do
echo "Step $i/10..."
sleep 1
done
echo "All steps complete."Combinando as armadilhas EXIT, ERR e INT
Em scripts de produção, normalmente você registra as três armadilhas juntas para abranger todos os caminhos de saída:
- EXIT — sempre é executada; cuida da limpeza de arquivos.
- ERR — registra o comando que falhou e o número da linha.
- INT — exibe uma mensagem de cancelamento e encerra com o código 130.
Como EXIT sempre é executada por último, sua lógica de limpeza precisa existir em apenas um lugar. Os manipuladores ERR e INT podem se concentrar no registro e na definição do estado, delegando a limpeza efetiva à EXIT ao chamar exit.
Use set -eEuo pipefail no início para que os erros nas funções também acionem ERR e para que pipefail capture falhas dentro das cadeias de comandos.
#!/usr/bin/env bash
set -eEuo pipefail
TMPFILE=$(mktemp /tmp/combined.XXXXXX)
ROLLBACK_NEEDED=false
cleanup() {
if [[ "$ROLLBACK_NEEDED" == true ]]; then
echo "Rolling back partial changes..." >&2
fi
rm -f "$TMPFILE"
echo "Cleanup done." >&2
}
on_error() {
ROLLBACK_NEEDED=true
echo "ERR at line $1: $BASH_COMMAND" >&2
}
on_interrupt() {
echo "Interrupted by user." >&2
exit 130
}
trap cleanup EXIT
trap 'on_error $LINENO' ERR
trap on_interrupt INT
echo "hello" > "$TMPFILE"
echo "Script finished successfully."Redefinindo e desativando armadilhas dinamicamente
Às vezes, você precisa modificar uma armadilha no meio do script — por exemplo, fazer uma limpeza única depois que uma seção crítica for concluída, para que as operações normais seguintes não acionem uma reversão desnecessária.
trap - SIGNALredefine o sinal para seu comportamento padrão integrado.trap '' SIGNALignora completamente o sinal (o processo não pode ser encerrado por ele).- Você pode registrar um novo manipulador novamente a qualquer momento; a última chamada a
trapprevalece.
Um padrão comum é atualizar a armadilha EXIT depois que uma operação arriscada é concluída com sucesso, trocando um manipulador de reversão por um simples manipulador de limpeza.
#!/usr/bin/env bash
set -euo pipefail
TMPFILE=$(mktemp /tmp/staged.XXXXXX)
rollback() {
echo "ROLLBACK: removing $TMPFILE" >&2
rm -f "$TMPFILE"
}
cleanup_only() {
echo "CLEANUP: removing $TMPFILE" >&2
rm -f "$TMPFILE"
}
# Start with rollback in case something fails during the risky phase
trap rollback EXIT
echo "Performing risky operation..."
echo "critical data" > "$TMPFILE"
# ... imagine more risky steps here ...
# Risky phase succeeded — switch to simple cleanup
trap cleanup_only EXIT
echo "Risky phase done. Now doing safe finalization."
cp "$TMPFILE" /tmp/staged_result.txt
echo "All done."Propagando códigos de saída pelas armadilhas
Uma armadilha sutil: se sua função de limpeza contiver comandos que podem falhar, o código de saída final do script poderá ser substituído. Preserve explicitamente o código de saída original.
Dentro de um manipulador de armadilha, $? contém o código de saída que acionou a armadilha. Capture-o na primeira linha do manipulador, antes que qualquer outro comando o altere.
#!/usr/bin/env bash
set -euo pipefail
TMPFILE=$(mktemp /tmp/exitcode.XXXXXX)
cleanup() {
local original_exit=$? # capture BEFORE any other command
echo "Cleaning up (original exit: $original_exit)" >&2
rm -f "$TMPFILE"
# Exit with the original code so callers see the real result
exit "$original_exit"
}
trap cleanup EXIT
echo "Doing work..."
echo "data" > "$TMPFILE"
# Simulate failure
if [[ ! -f /tmp/required_marker ]]; then
echo "Required marker missing!" >&2
exit 2
fi
echo "Done."Armadilhas em funções e subshells
Entender a herança de armadilhas é fundamental para escrever scripts corretos:
- Funções são executadas no mesmo shell e herdam as armadilhas — mas ERR só é propagada para as funções quando
set -E(errtrace) está ativo. - Subshells (
( ... )) começam com uma cópia das armadilhas do processo pai, mas as alterações dentro do subshell não afetam o processo pai. - Tarefas em segundo plano (
cmd &) são subshells e não herdam as armadilhas INT/TERM do processo pai (recebem a disposição padrão do sinal).
Regra prática: mantenha o registro das armadilhas no corpo principal do script, use set -E quando precisar que ERR seja propagada para as funções e nunca dependa da herança de armadilhas através de &.
#!/usr/bin/env bash
set -eEuo pipefail # -E = errtrace: ERR propagates into functions
on_error() {
echo "ERR in function or main at line $1" >&2
}
trap 'on_error $LINENO' ERR
risky_function() {
echo "Inside risky_function"
ls /nonexistent_path # triggers ERR — works because of set -E
}
# Subshell: has its own copy; changes don't affect parent
(
trap '' ERR # disable ERR only inside subshell
ls /nonexistent_path 2>/dev/null || true
echo "Subshell completed without triggering parent ERR"
)
risky_functionPadrão do mundo real: substituição atômica de arquivos
Um padrão clássico de Bash defensivo é a substituição atômica de arquivos: grave em um arquivo temporário e depois renomeie-o para o local correto. Se algo falhar antes da renomeação, o original permanece intacto. As armadilhas tornam esse processo robusto.
Etapas:
- Crie um arquivo temporário no mesmo sistema de arquivos que o destino (para que
mvseja atômico). - Registre uma armadilha EXIT para remover o arquivo temporário se sairmos antecipadamente.
- Grave e valide o novo conteúdo.
- Renomeie atomicamente (
mv) — somente agora o original é substituído. - A armadilha EXIT remove o arquivo temporário apenas se ele ainda existir (após um
mvbem-sucedido, ele não existe mais).
#!/usr/bin/env bash
set -euo pipefail
DEST="/tmp/important_config.conf"
TMPFILE=$(mktemp "$(dirname "$DEST")/.tmp.XXXXXX")
cleanup() {
rm -f "$TMPFILE" # no-op if mv already moved it
}
trap cleanup EXIT
# Write new content to temp file
cat > "$TMPFILE" <<'EOF'
[settings]
version=2
mode=production
EOF
# Validate before replacing
if ! grep -q 'version=' "$TMPFILE"; then
echo "Validation failed — aborting replacement" >&2
exit 1
fi
# Atomic rename — DEST is replaced only here
mv "$TMPFILE" "$DEST"
echo "Config updated atomically: $DEST"Verificação de conhecimento: herança da armadilha ERR
Teste sua compreensão de como a armadilha ERR se comporta com funções no Bash.
Recapitulação: manipuladores de armadilhas para limpeza e sinais
Nesta lição, você aprendeu a tornar os scripts Bash resilientes por meio de manipuladores de armadilhas:
- EXIT é sua rede de segurança universal — registre-a imediatamente depois de criar qualquer recurso temporário e sempre capture
$?primeiro para preservar o código de saída original. - ERR permite registrar falhas com
$LINENOe$BASH_COMMAND; useset -Epara que as funções a herdem. - INT trata Ctrl+C com segurança — exiba uma mensagem e encerre com o código 130 para permitir que EXIT faça a limpeza.
- Reúna os recursos temporários em uma matriz e remova todos eles em uma única função de limpeza.
- Use
trap - SIGNALpara redefinir etrap '' SIGNALpara ignorar sinais dinamicamente. - O padrão de renomeação atômica (
mktemp→ gravar → validar →mv), combinado com uma armadilha EXIT, garante que gravações parciais nunca corrompam o arquivo de destino.
Combinar set -eEuo pipefail com armadilhas bem posicionadas oferece uma base defensiva sólida para qualquer script Bash de produção.
Perguntas Frequentes
A aula “Manipuladores trap para limpeza e sinais” é grátis?
Sim — o texto completo de “Manipuladores trap para limpeza e sinais” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de DevOps Bootcamp, atualize para CoddyKit PRO. O curso de DevOps Bootcamp inclui 4 aulas no total.
O que vou aprender em “Manipuladores trap para limpeza e sinais”?
Registre traps para EXIT, ERR e INT a fim de remover arquivos temporários e reverter trabalhos parciais de forma confiável. Você pratica DevOps Bootcamp com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.
Preciso ter experiência prévia para começar DevOps Bootcamp?
Nenhuma experiência prévia é necessária. DevOps Bootcamp no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 2 de 4.
Quanto tempo leva a aula “Manipuladores trap para limpeza e sinais”?
A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.
Posso escrever e executar código nesta aula de DevOps Bootcamp?
Sim. Cada aula de DevOps Bootcamp inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.
Todas as aulas deste curso
- Modo estrito com set -euo pipefail
- Manipuladores trap para limpeza e sinais
- Arquivos temporários seguros e diretórios de bloqueio
- Scripts idempotentes e lógica de novas tentativas com espera progressiva