0Pricing
DevOps Bootcamp · Aula

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 '' SIGNAL ignora esse sinal (manipulador vazio).
  • trap - SIGNAL redefine 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 - PIPE

A 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 mktemp para 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 $LINENO e $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 - SIGNAL redefine o sinal para seu comportamento padrão integrado.
  • trap '' SIGNAL ignora 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 trap prevalece.

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_function

Padrã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:

  1. Crie um arquivo temporário no mesmo sistema de arquivos que o destino (para que mv seja atômico).
  2. Registre uma armadilha EXIT para remover o arquivo temporário se sairmos antecipadamente.
  3. Grave e valide o novo conteúdo.
  4. Renomeie atomicamente (mv) — somente agora o original é substituído.
  5. A armadilha EXIT remove o arquivo temporário apenas se ele ainda existir (após um mv bem-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 $LINENO e $BASH_COMMAND; use set -E para 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 - SIGNAL para redefinir e trap '' SIGNAL para 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

  1. Modo estrito com set -euo pipefail
  2. Manipuladores trap para limpeza e sinais
  3. Arquivos temporários seguros e diretórios de bloqueio
  4. Scripts idempotentes e lógica de novas tentativas com espera progressiva
← Voltar para DevOps Bootcamp