Controladores trap para limpieza y señales
Registre traps EXIT, ERR e INT para eliminar archivos temporales y revertir de forma fiable el trabajo parcial.
Controladores trap para limpieza y señales es una lección gratuita de Linux Command Line & Bash Scripting Mastery en CoddyKit. Esta es la lección 2 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de Linux Command Line & Bash Scripting Mastery, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de Linux Command Line & Bash Scripting Mastery incluye 4 lecciones en total.
Por qué son importantes los manejadores de trap
Cuando un script de Bash termina — ya sea con normalidad, debido a un error o porque el usuario ha pulsado Ctrl+C— puede dejar archivos temporales, datos escritos parcialmente o un estado incoherente. Sin limpieza, estos residuos se acumulan y provocan problemas difíciles de depurar.
Los manejadores de trap resuelven este problema registrando una función o un comando que Bash ejecuta automáticamente al recibir una señal o pseudoseñal específica.
- EXIT — se activa cada vez que el script termina, por cualquier motivo
- ERR — se activa después de que cualquier comando devuelva un estado de salida distinto de cero
- INT — se activa cuando el usuario envía SIGINT (Ctrl+C)
- TERM — se activa con SIGTERM (por ejemplo, mediante
kill)
Un script bien escrito registra estas traps al principio, antes de iniciar cualquier operación arriesgada.
Sintaxis del comando integrado trap
El comando integrado trap registra un manejador para una o más señales o pseudoseñales. La sintaxis general es:
trap 'command_or_function' SIGNAL [SIGNAL...]
Reglas principales:
- El primer argumento es una cadena entre comillas de código de shell (o el nombre de una función) que se ejecutará cuando se active la señal.
- Puede enumerar varias señales después del manejador.
trap '' SIGNALignora esa señal (manejador vacío).trap - SIGNALrestablece la señal a su comportamiento predeterminado.
Las traps se heredan en las funciones llamadas en el mismo shell, pero no en los subshells iniciados con ( ) o &.
#!/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 - PIPELa trap EXIT: su red de seguridad
La pseudoseñal EXIT es la trap más importante que debe registrar. Se activa cuando el shell termina por cualquier motivo: finalización normal, exit N, un error no controlado o una señal. Esto la hace ideal para eliminar archivos temporales.
Práctica recomendada:
- Cree archivos temporales con
mktemppara que el nombre sea único e impredecible. - Guarde la ruta en una variable inmediatamente.
- Registre la trap EXIT justo después de crearlo para que la limpieza se ejecute siempre.
El código de salida del script se conserva: el código de salida del manejador de trap se ignora, a menos que llame explícitamente a exit dentro de él.
#!/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."Gestión de varios archivos y directorios temporales
Los scripts reales suelen crear varios recursos temporales: archivos, directorios, FIFOs y archivos de bloqueo. En lugar de realizar el seguimiento de cada uno por separado, reúnalos en un array y elimínelos todos mediante una única función de limpieza.
Este patrón se amplía fácilmente: cada vez que cree un nuevo recurso temporal, añádalo al array. La trap EXIT siempre gestiona la lista completa, independientemente del punto en el que termine el script.
#!/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."La trap ERR: captura de fallos
La pseudoseñal ERR se activa después de que cualquier comando simple devuelva un estado de salida distinto de cero, siempre que la opción de shell set -e (errexit) esté activa o el comando no se encuentre dentro de una construcción compuesta if, while o ||/&&.
Use la trap ERR para:
- Registrar qué línea ha fallado mediante
$LINENOy$BASH_COMMAND. - Activar la lógica de reversión antes de que se ejecute la trap EXIT.
- Emitir mensajes de error estructurados en stderr.
Importante: las traps ERR no se activan dentro de las funciones, a menos que ejecute explícitamente set -E (errtrace) para que las funciones hereden la trap ERR del shell que las llama.
#!/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."La trap INT: Ctrl+C controlado
Al pulsar Ctrl+C se envía SIGINT al grupo de procesos en primer plano. Sin una trap, Bash termina inmediatamente y puede dejar trabajo parcialmente realizado.
Registrar una trap INT le permite:
- Mostrar un mensaje de cancelación claro para el usuario.
- Realizar cualquier reversión intermedia antes de delegar en la trap EXIT.
- Terminar con un código de estado significativo (
130es el código convencional para SIGINT:128 + 2).
Cuando termine el manejador INT, llame a exit 130 para propagar el código de salida correcto. La trap EXIT se ejecutará automáticamente a continuación.
#!/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."Combinación de las traps EXIT, ERR e INT
En los scripts de producción, normalmente registra las tres traps juntas para cubrir todas las vías de salida:
- EXIT — siempre se ejecuta; gestiona la limpieza de archivos.
- ERR — registra el comando que ha fallado y el número de línea.
- INT — muestra un mensaje de cancelación y termina con el código 130.
Como EXIT siempre se ejecuta al final, la lógica de limpieza solo debe estar en un lugar. Los manejadores ERR e INT pueden centrarse en registrar información y establecer el estado, y delegar la limpieza real en EXIT mediante una llamada a exit.
Use set -eEuo pipefail al principio para que los errores en las funciones también activen ERR y para que pipefail detecte los fallos dentro de las tuberías.
#!/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."Restablecimiento y desactivación dinámica de traps
A veces necesita modificar una trap durante la ejecución del script; por ejemplo, para realizar una limpieza puntual cuando finalice una sección crítica y evitar que las operaciones normales posteriores activen una reversión innecesaria.
trap - SIGNALrestablece la señal a su comportamiento predeterminado integrado.trap '' SIGNALignora completamente la señal (el proceso no puede ser terminado mediante ella).- Puede volver a registrar un nuevo manejador en cualquier momento; prevalece la última llamada a
trap.
Un patrón habitual consiste en actualizar la trap EXIT una vez que una operación arriesgada se realiza correctamente, sustituyendo el manejador de reversión por un manejador de limpieza sencillo.
#!/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."Propagación de códigos de salida mediante traps
Existe un problema sutil: si la función de limpieza contiene comandos que pueden fallar, el código de salida final del script podría sobrescribirse. Conserve explícitamente el código de salida original.
Dentro de un manejador de trap, $? contiene el código de salida que activó la trap. Captúrelo en la primera línea del manejador, antes de que cualquier otro comando lo modifique.
#!/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."Traps en funciones y subshells
Comprender la herencia de las traps es fundamental para escribir scripts correctos:
- Las funciones se ejecutan en el mismo shell y heredan las traps, pero ERR solo se propaga a las funciones cuando
set -E(errtrace) está activo. - Los subshells (
( ... )) comienzan con una copia de las traps del proceso padre, pero los cambios realizados dentro del subshell no afectan al padre. - Los trabajos en segundo plano (
cmd &) son subshells y no heredan las traps INT/TERM del proceso padre (reciben la disposición predeterminada de las señales).
Regla práctica: mantenga el registro de traps en el cuerpo principal del script, use set -E cuando necesite que ERR se propague a las funciones y no dependa nunca de la herencia de traps a travé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_functionPatrón del mundo real: sustitución atómica de archivos
Un patrón clásico de Bash defensivo es la sustitución atómica de archivos: escribir en un archivo temporal y cambiarle después el nombre para colocarlo en su ubicación definitiva. Si algo falla antes del cambio de nombre, el archivo original permanece intacto. Las traps hacen que este proceso sea robusto.
Pasos:
- Cree un archivo temporal en el mismo sistema de archivos que el destino (para que
mvsea atómico). - Registre una trap EXIT para eliminar el archivo temporal si terminamos antes de tiempo.
- Escriba y valide el contenido nuevo.
- Cambie el nombre de forma atómica (
mv); solo en este momento se reemplaza el original. - La trap EXIT elimina el archivo temporal únicamente si todavía existe (después de un
mvcorrecto, ya no existe).
#!/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"Comprobación de conocimientos: herencia de la trap ERR
Compruebe su comprensión del comportamiento de la trap ERR con las funciones en Bash.
Repaso: manejadores de trap para limpieza y señales
En esta lección ha aprendido a hacer que los scripts de Bash sean resistentes mediante manejadores de trap:
- EXIT es su red de seguridad universal: regístrela inmediatamente después de crear cualquier recurso temporal y capture siempre primero
$?para conservar el código de salida original. - ERR permite registrar los fallos con
$LINENOy$BASH_COMMAND; useset -Epara que las funciones la hereden. - INT gestiona Ctrl+C correctamente: muestre un mensaje y termine con el código 130 para que EXIT realice la limpieza.
- Reúna los recursos temporales en un array y elimínelos todos mediante una única función de limpieza.
- Use
trap - SIGNALpara restablecer las señales ytrap '' SIGNALpara ignorarlas dinámicamente. - El patrón de cambio de nombre atómico (
mktemp→ escribir → validar →mv), combinado con una trap EXIT, garantiza que las escrituras parciales nunca corrompan el archivo de destino.
Combinar set -eEuo pipefail con traps bien ubicadas proporciona una base defensiva sólida para cualquier script de Bash de producción.
Preguntas frecuentes
¿La lección «Controladores trap para limpieza y señales» es gratis?
Sí — el texto completo de «Controladores trap para limpieza y señales» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de Linux Command Line & Bash Scripting Mastery, actualiza a CoddyKit PRO. El curso de Linux Command Line & Bash Scripting Mastery incluye 4 lecciones en total.
¿Qué aprenderé en «Controladores trap para limpieza y señales»?
Registre traps EXIT, ERR e INT para eliminar archivos temporales y revertir de forma fiable el trabajo parcial. Practicas Linux Command Line & Bash Scripting Mastery con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.
¿Necesito experiencia previa para empezar Linux Command Line & Bash Scripting Mastery?
No se requiere experiencia previa. Linux Command Line & Bash Scripting Mastery en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 2 de 4.
¿Cuánto tiempo toma la lección «Controladores trap para limpieza y señales»?
La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.
¿Puedo escribir y ejecutar código en esta lección de Linux Command Line & Bash Scripting Mastery?
Sí. Cada lección de Linux Command Line & Bash Scripting Mastery incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.
Todas las lecciones de este curso
- Modo estricto con set -euo pipefail
- Controladores trap para limpieza y señales
- Archivos temporales seguros y directorios de bloqueo
- Scripts idempotentes y lógica de reintento con backoff