Bash Trap Command: Complete Guide to Signal Handling in Scripts
Table of Contents
1. [Introduction to Trap Command](#introduction-to-trap-command) 2. [Basic Syntax and Usage](#basic-syntax-and-usage) 3. [Signal Types and Numbers](#signal-types-and-numbers) 4. [Trap Command Variations](#trap-command-variations) 5. [Practical Examples](#practical-examples) 6. [Best Practices](#best-practices) 7. [Advanced Usage Scenarios](#advanced-usage-scenarios) 8. [Troubleshooting Common Issues](#troubleshooting-common-issues)
Introduction to Trap Command
The trap command in Bash is a powerful built-in utility that allows scripts to handle signals gracefully. Signals are software interrupts that can be sent to running processes to communicate various events or requests. The trap command enables scripts to define custom actions that should be executed when specific signals are received, providing a mechanism for cleanup operations, error handling, and controlled script termination.
When a script runs without trap handlers, receiving certain signals like SIGINT (Ctrl+C) or SIGTERM will cause immediate termination, potentially leaving temporary files, open connections, or incomplete operations. The trap command addresses this limitation by allowing developers to specify exactly what should happen when these signals are received.
Why Use Trap Commands
The primary purposes of using trap commands include:
- Cleanup Operations: Remove temporary files, close database connections, or release system resources - Graceful Shutdown: Ensure processes terminate cleanly rather than abruptly - Error Handling: Execute specific actions when errors occur or scripts exit unexpectedly - State Preservation: Save current work or system state before termination - Logging: Record important information about script execution and termination reasons
Basic Syntax and Usage
The fundamental syntax of the trap command follows this pattern:
`bash
trap 'command_list' signal_list
`
Syntax Components
| Component | Description | Example |
|-----------|-------------|---------|
| trap | The command keyword | trap |
| 'command_list' | Commands to execute (quoted) | 'echo "Cleaning up"; rm temp.txt' |
| signal_list | Space-separated list of signals | SIGINT SIGTERM EXIT |
Basic Command Structure
`bash
Single command for single signal
trap 'echo "Script interrupted"' INTMultiple commands for single signal
trap 'echo "Cleaning up"; rm -f /tmp/script.$; exit 1' INTSingle command for multiple signals
trap 'cleanup_function' INT TERM EXITFunction call as trap action
trap cleanup_function INT TERM`Removing Traps
`bash
Remove trap for specific signal
trap - INTRemove trap for multiple signals
trap - INT TERM EXITReset to default behavior
trap INT TERM`Listing Current Traps
`bash
List all current traps
trapList trap for specific signal
trap -p INT`Signal Types and Numbers
Understanding different signal types is crucial for effective trap usage. Signals can be specified by name, number, or symbolic name.
Common Signal Reference Table
| Signal Name | Signal Number | Description | Default Action | Trappable | |-------------|---------------|-------------|----------------|-----------| | SIGHUP | 1 | Hangup detected on controlling terminal | Terminate | Yes | | SIGINT | 2 | Interrupt from keyboard (Ctrl+C) | Terminate | Yes | | SIGQUIT | 3 | Quit from keyboard (Ctrl+\) | Core dump | Yes | | SIGKILL | 9 | Kill signal | Terminate | No | | SIGTERM | 15 | Termination signal | Terminate | Yes | | SIGSTOP | 19 | Stop process | Stop | No | | SIGTSTP | 20 | Terminal stop signal (Ctrl+Z) | Stop | Yes | | SIGUSR1 | 10 | User-defined signal 1 | Terminate | Yes | | SIGUSR2 | 12 | User-defined signal 2 | Terminate | Yes |
Special Trap Conditions
| Condition | Description | Usage Example |
|-----------|-------------|---------------|
| EXIT | Script exit (normal or abnormal) | trap 'echo "Script ending"' EXIT |
| ERR | Command returns non-zero exit status | trap 'echo "Error occurred"' ERR |
| DEBUG | Before each command execution | trap 'echo "Debug: $BASH_COMMAND"' DEBUG |
| RETURN | Shell function or sourced script returns | trap 'echo "Function returned"' RETURN |
Signal Specification Methods
`bash
By signal name (recommended)
trap 'cleanup' SIGINT SIGTERMBy signal name without SIG prefix
trap 'cleanup' INT TERMBy signal number
trap 'cleanup' 2 15Mixed specification (not recommended but valid)
trap 'cleanup' INT 15 SIGQUIT`Trap Command Variations
Function-Based Traps
Using functions with traps provides better organization and reusability:
`bash
#!/bin/bash
Define cleanup function
cleanup() { echo "Performing cleanup operations..." # Remove temporary files rm -f /tmp/script_temp_$ rm -f /tmp/script_lock_$ # Close file descriptors exec 3>&- exec 4>&- # Kill background processes jobs -p | xargs -r kill echo "Cleanup completed" exit 0 }Set trap to call function
trap cleanup INT TERM EXITScript main logic
echo "Script starting with PID $" touch /tmp/script_temp_$ touch /tmp/script_lock_$Simulate long-running process
for i in {1..100}; do echo "Processing item $i" sleep 1 done`Conditional Trap Actions
Traps can include conditional logic to handle different scenarios:
`bash
#!/bin/bash
interrupted=false
handle_interrupt() { if [ "$interrupted" = "false" ]; then echo "First interrupt received. Press Ctrl+C again to force quit." interrupted=true return else echo "Second interrupt received. Forcing quit..." cleanup_and_exit fi }
cleanup_and_exit() { echo "Performing emergency cleanup..." # Emergency cleanup code here exit 1 }
trap handle_interrupt INT trap cleanup_and_exit TERM
Reset interrupt flag periodically
while true; do echo "Working..." sleep 5 interrupted=false done`Nested Trap Handling
Complex scripts may require different trap behaviors in different contexts:
`bash
#!/bin/bash
Global cleanup function
global_cleanup() { echo "Global cleanup executing..." rm -f /tmp/global_* }Database operation cleanup
db_cleanup() { echo "Database cleanup executing..." # Close database connections mysql -e "KILL CONNECTION_ID();" 2>/dev/null || true global_cleanup }File operation cleanup
file_cleanup() { echo "File cleanup executing..." # Unlock files, remove temporary files rm -f /tmp/file_lock_* global_cleanup }Set initial trap
trap global_cleanup EXIT INT TERMFunction that changes trap behavior
database_operations() { # Override trap for database operations trap db_cleanup EXIT INT TERM echo "Starting database operations..." # Database work here sleep 10 # Restore original trap trap global_cleanup EXIT INT TERM }Function that changes trap behavior
file_operations() { # Override trap for file operations trap file_cleanup EXIT INT TERM echo "Starting file operations..." # File work here sleep 10 # Restore original trap trap global_cleanup EXIT INT TERM }Main script execution
database_operations file_operations`Practical Examples
Example 1: Basic Cleanup Script
`bash
#!/bin/bash
basic_cleanup.sh - Demonstrates basic trap usage
Global variables
TEMP_DIR="/tmp/script_$" LOG_FILE="/var/log/script.log" PID_FILE="/var/run/script.pid"Cleanup function
cleanup() { echo "$(date): Cleanup initiated" >> "$LOG_FILE" # Remove temporary directory if [ -d "$TEMP_DIR" ]; then rm -rf "$TEMP_DIR" echo "$(date): Removed temporary directory $TEMP_DIR" >> "$LOG_FILE" fi # Remove PID file if [ -f "$PID_FILE" ]; then rm -f "$PID_FILE" echo "$(date): Removed PID file $PID_FILE" >> "$LOG_FILE" fi echo "$(date): Cleanup completed" >> "$LOG_FILE" exit 0 }Set trap
trap cleanup INT TERM EXITInitialize script
echo $ > "$PID_FILE" mkdir -p "$TEMP_DIR" echo "$(date): Script started with PID $" >> "$LOG_FILE"Main script logic
echo "Script running. Press Ctrl+C to test cleanup..."Simulate work
for i in {1..60}; do echo "Working... $i/60" touch "$TEMP_DIR/work_file_$i" sleep 1 doneecho "Script completed normally"
`
Example 2: Advanced Error Handling
`bash
#!/bin/bash
error_handling.sh - Advanced error handling with traps
Enable strict error handling
set -euo pipefailGlobal variables
SCRIPT_NAME=$(basename "$0") SCRIPT_PID=$ ERROR_LOG="/var/log/${SCRIPT_NAME}_errors.log" OPERATION_LOG="/var/log/${SCRIPT_NAME}_operations.log"Error handling function
handle_error() { local exit_code=$? local line_number=$1 local command="$2" echo "$(date): ERROR - Script $SCRIPT_NAME failed" >> "$ERROR_LOG" echo "$(date): Exit Code: $exit_code" >> "$ERROR_LOG" echo "$(date): Line Number: $line_number" >> "$ERROR_LOG" echo "$(date): Failed Command: $command" >> "$ERROR_LOG" echo "$(date): PID: $SCRIPT_PID" >> "$ERROR_LOG" echo "---" >> "$ERROR_LOG" # Perform cleanup cleanup_on_error exit $exit_code }Cleanup function for errors
cleanup_on_error() { echo "$(date): Emergency cleanup initiated" >> "$OPERATION_LOG" # Kill any background jobs jobs -p | while read pid; do kill -TERM "$pid" 2>/dev/null || true done # Remove lock files rm -f /tmp/script_lock_* echo "$(date): Emergency cleanup completed" >> "$OPERATION_LOG" }Normal cleanup function
normal_cleanup() { echo "$(date): Normal cleanup initiated" >> "$OPERATION_LOG" # Graceful cleanup operations echo "$(date): Script completed successfully" >> "$OPERATION_LOG" }Set traps
trap 'handle_error ${LINENO} "$BASH_COMMAND"' ERR trap normal_cleanup EXIT trap cleanup_on_error INT TERMMain script operations
echo "$(date): Starting operations..." >> "$OPERATION_LOG"Simulate operations that might fail
risky_operation() { echo "Performing risky operation..." # This might fail if [ $((RANDOM % 3)) -eq 0 ]; then false # Simulate failure fi echo "Risky operation completed successfully" }Execute operations
for i in {1..5}; do echo "Operation $i starting..." risky_operation echo "Operation $i completed" sleep 1 done`Example 3: Resource Management Script
`bash
#!/bin/bash
resource_manager.sh - Comprehensive resource management
Resource tracking arrays
declare -a TEMP_FILES=() declare -a TEMP_DIRS=() declare -a BACKGROUND_PIDS=() declare -a OPEN_FDS=()Add resource tracking functions
track_temp_file() { TEMP_FILES+=("$1") }track_temp_dir() { TEMP_DIRS+=("$1") }
track_background_pid() { BACKGROUND_PIDS+=("$1") }
track_file_descriptor() { OPEN_FDS+=("$1") }
Comprehensive cleanup function
comprehensive_cleanup() { echo "Starting comprehensive cleanup..." # Clean up background processes if [ ${#BACKGROUND_PIDS[@]} -gt 0 ]; then echo "Terminating ${#BACKGROUND_PIDS[@]} background processes..." for pid in "${BACKGROUND_PIDS[@]}"; do if kill -0 "$pid" 2>/dev/null; then echo "Terminating process $pid" kill -TERM "$pid" 2>/dev/null || true sleep 1 kill -KILL "$pid" 2>/dev/null || true fi done fi # Close file descriptors if [ ${#OPEN_FDS[@]} -gt 0 ]; then echo "Closing ${#OPEN_FDS[@]} file descriptors..." for fd in "${OPEN_FDS[@]}"; do eval "exec ${fd}>&-" 2>/dev/null || true done fi # Remove temporary files if [ ${#TEMP_FILES[@]} -gt 0 ]; then echo "Removing ${#TEMP_FILES[@]} temporary files..." for file in "${TEMP_FILES[@]}"; do rm -f "$file" 2>/dev/null || true done fi # Remove temporary directories if [ ${#TEMP_DIRS[@]} -gt 0 ]; then echo "Removing ${#TEMP_DIRS[@]} temporary directories..." for dir in "${TEMP_DIRS[@]}"; do rm -rf "$dir" 2>/dev/null || true done fi echo "Comprehensive cleanup completed" }Set trap
trap comprehensive_cleanup EXIT INT TERMExample usage of resource tracking
echo "Creating resources..."Create and track temporary files
temp_file1=$(mktemp) temp_file2=$(mktemp) track_temp_file "$temp_file1" track_temp_file "$temp_file2"Create and track temporary directories
temp_dir1=$(mktemp -d) temp_dir2=$(mktemp -d) track_temp_dir "$temp_dir1" track_temp_dir "$temp_dir2"Start and track background processes
sleep 30 & track_background_pid $!sleep 60 & track_background_pid $!
Open and track file descriptors
exec 3> "$temp_file1" exec 4> "$temp_file2" track_file_descriptor 3 track_file_descriptor 4Simulate script work
echo "Script working with resources..." echo "Data1" >&3 echo "Data2" >&4Create some files in temp directories
touch "$temp_dir1/test1.txt" touch "$temp_dir2/test2.txt"echo "Resources created. Script will clean up on exit." sleep 10
echo "Script work completed"
`
Best Practices
Design Principles
| Principle | Description | Implementation | |-----------|-------------|----------------| | Idempotency | Cleanup should be safe to run multiple times | Check resource existence before cleanup | | Reliability | Cleanup should not fail even if resources are missing | Use conditional checks and error suppression | | Completeness | All created resources should be tracked and cleaned | Maintain resource lists or use systematic naming | | Speed | Cleanup should be fast to avoid delays | Prioritize critical cleanup operations |
Trap Implementation Guidelines
`bash
Good: Use functions for complex cleanup
cleanup() { # Organized cleanup logic cleanup_files cleanup_processes cleanup_network } trap cleanup EXIT INT TERMAvoid: Inline complex logic
trap 'rm -f file1; kill $pid1; rm -f file2; kill $pid2; ...' EXIT
``bash
Good: Check resource existence
cleanup() { if [ -f "$LOCK_FILE" ]; then rm -f "$LOCK_FILE" fi if [ -n "$BACKGROUND_PID" ] && kill -0 "$BACKGROUND_PID" 2>/dev/null; then kill "$BACKGROUND_PID" fi }Avoid: Assuming resources exist
cleanup() {
rm "$LOCK_FILE" # May fail if file doesn't exist
kill "$BACKGROUND_PID" # May fail if process doesn't exist
}
`Error Handling in Traps
`bash
Robust trap function with error handling
cleanup() { local exit_code=$? # Disable further traps to prevent recursion trap - EXIT INT TERM # Perform cleanup with error suppression { rm -rf "$TEMP_DIR" kill -TERM "$BACKGROUND_PID" rm -f "$LOCK_FILE" } 2>/dev/null || true # Log cleanup completion echo "Cleanup completed with original exit code: $exit_code" >&2 # Exit with original code exit $exit_code }`Signal Priority Handling
`bash
Handle different signals with appropriate responses
handle_sigterm() { echo "SIGTERM received - graceful shutdown" graceful_shutdown }handle_sigint() { echo "SIGINT received - user interruption" user_interruption_cleanup }
handle_sigquit() { echo "SIGQUIT received - immediate termination" emergency_cleanup }
trap handle_sigterm TERM
trap handle_sigint INT
trap handle_sigquit QUIT
`
Advanced Usage Scenarios
Scenario 1: Database Connection Management
`bash
#!/bin/bash
db_connection_manager.sh
Database configuration
DB_HOST="localhost" DB_USER="app_user" DB_NAME="application_db" CONNECTION_ID=""Establish database connection
establish_db_connection() { echo "Establishing database connection..." # Get connection ID for cleanup CONNECTION_ID=$(mysql -h "$DB_HOST" -u "$DB_USER" -D "$DB_NAME" \ -e "SELECT CONNECTION_ID();" -s -N 2>/dev/null) if [ -n "$CONNECTION_ID" ]; then echo "Database connected with ID: $CONNECTION_ID" return 0 else echo "Failed to establish database connection" return 1 fi }Database cleanup function
cleanup_database() { if [ -n "$CONNECTION_ID" ]; then echo "Closing database connection ID: $CONNECTION_ID" # Rollback any pending transactions mysql -h "$DB_HOST" -u "$DB_USER" -D "$DB_NAME" \ -e "ROLLBACK;" 2>/dev/null || true # Close the connection mysql -h "$DB_HOST" -u "$DB_USER" -D "$DB_NAME" \ -e "KILL $CONNECTION_ID;" 2>/dev/null || true echo "Database connection closed" fi }Set trap for database cleanup
trap cleanup_database EXIT INT TERMMain database operations
if establish_db_connection; then echo "Performing database operations..." # Begin transaction mysql -h "$DB_HOST" -u "$DB_USER" -D "$DB_NAME" -e "BEGIN;" # Simulate database work for i in {1..10}; do echo "Database operation $i" mysql -h "$DB_HOST" -u "$DB_USER" -D "$DB_NAME" \ -e "INSERT INTO activity_log (message) VALUES ('Operation $i');" sleep 1 done # Commit transaction mysql -h "$DB_HOST" -u "$DB_USER" -D "$DB_NAME" -e "COMMIT;" echo "Database operations completed successfully" else echo "Cannot proceed without database connection" exit 1 fi`Scenario 2: Multi-Process Coordination
`bash
#!/bin/bash
multi_process_coordinator.sh
Process tracking
declare -a WORKER_PIDS=() COORDINATOR_PID=$ SHARED_MEMORY="/tmp/coordinator_$"Initialize shared resources
initialize_shared_resources() { mkdir -p "$SHARED_MEMORY" echo "0" > "$SHARED_MEMORY/completed_tasks" echo "active" > "$SHARED_MEMORY/status" }Worker process function
worker_process() { local worker_id=$1 local tasks=$2 echo "Worker $worker_id starting with $tasks tasks" for ((i=1; i<=tasks; i++)); do # Simulate work sleep $((RANDOM % 3 + 1)) # Update shared counter atomically ( flock -x 200 local completed=$(cat "$SHARED_MEMORY/completed_tasks") echo $((completed + 1)) > "$SHARED_MEMORY/completed_tasks" ) 200>"$SHARED_MEMORY/lock" echo "Worker $worker_id completed task $i" done echo "Worker $worker_id finished" }Cleanup function for coordinator
cleanup_coordinator() { echo "Coordinator cleanup initiated" # Signal all workers to stop echo "stopping" > "$SHARED_MEMORY/status" # Terminate worker processes for pid in "${WORKER_PIDS[@]}"; do if kill -0 "$pid" 2>/dev/null; then echo "Terminating worker process $pid" kill -TERM "$pid" 2>/dev/null || true fi done # Wait for workers to terminate gracefully local timeout=10 while [ $timeout -gt 0 ] && [ ${#WORKER_PIDS[@]} -gt 0 ]; do local remaining_pids=() for pid in "${WORKER_PIDS[@]}"; do if kill -0 "$pid" 2>/dev/null; then remaining_pids+=("$pid") fi done WORKER_PIDS=("${remaining_pids[@]}") if [ ${#WORKER_PIDS[@]} -eq 0 ]; then break fi sleep 1 ((timeout--)) done # Force kill any remaining workers for pid in "${WORKER_PIDS[@]}"; do kill -KILL "$pid" 2>/dev/null || true done # Clean up shared resources rm -rf "$SHARED_MEMORY" echo "Coordinator cleanup completed" }Set trap for coordinator
trap cleanup_coordinator EXIT INT TERMInitialize
initialize_shared_resourcesStart worker processes
echo "Starting worker processes..." for worker_id in {1..5}; do worker_process "$worker_id" 10 & WORKER_PIDS+=($!) echo "Started worker $worker_id with PID ${WORKER_PIDS[-1]}" doneMonitor progress
echo "Monitoring worker progress..." total_expected=$((5 * 10)) # 5 workers, 10 tasks eachwhile true; do if [ ! -f "$SHARED_MEMORY/completed_tasks" ]; then break fi completed=$(cat "$SHARED_MEMORY/completed_tasks" 2>/dev/null || echo "0") echo "Progress: $completed/$total_expected tasks completed" # Check if all workers are done active_workers=0 for pid in "${WORKER_PIDS[@]}"; do if kill -0 "$pid" 2>/dev/null; then ((active_workers++)) fi done if [ $active_workers -eq 0 ]; then echo "All workers completed" break fi sleep 2 done
echo "Coordination completed successfully"
`
Scenario 3: Network Service Management
`bash
#!/bin/bash
network_service_manager.sh
Service configuration
SERVICE_PORT=8080 SERVICE_PID="" LOG_FILE="/var/log/service_manager.log"Start network service
start_service() { echo "$(date): Starting network service on port $SERVICE_PORT" >> "$LOG_FILE" # Start a simple HTTP server (example) python3 -m http.server "$SERVICE_PORT" & SERVICE_PID=$! # Wait for service to be ready local retries=10 while [ $retries -gt 0 ]; do if netstat -ln | grep -q ":$SERVICE_PORT "; then echo "$(date): Service started successfully with PID $SERVICE_PID" >> "$LOG_FILE" return 0 fi sleep 1 ((retries--)) done echo "$(date): Failed to start service" >> "$LOG_FILE" return 1 }Stop network service
stop_service() { if [ -n "$SERVICE_PID" ] && kill -0 "$SERVICE_PID" 2>/dev/null; then echo "$(date): Stopping service with PID $SERVICE_PID" >> "$LOG_FILE" # Graceful shutdown kill -TERM "$SERVICE_PID" 2>/dev/null || true # Wait for graceful shutdown local timeout=30 while [ $timeout -gt 0 ] && kill -0 "$SERVICE_PID" 2>/dev/null; do sleep 1 ((timeout--)) done # Force kill if necessary if kill -0 "$SERVICE_PID" 2>/dev/null; then echo "$(date): Force killing service" >> "$LOG_FILE" kill -KILL "$SERVICE_PID" 2>/dev/null || true fi echo "$(date): Service stopped" >> "$LOG_FILE" fi }Cleanup network resources
cleanup_network() { echo "$(date): Network cleanup initiated" >> "$LOG_FILE" # Stop the service stop_service # Clean up any remaining connections on the port local connections=$(netstat -an | grep ":$SERVICE_PORT " | wc -l) if [ "$connections" -gt 0 ]; then echo "$(date): Found $connections remaining connections" >> "$LOG_FILE" # Additional cleanup if needed fi # Remove any socket files rm -f "/tmp/service_socket_$" echo "$(date): Network cleanup completed" >> "$LOG_FILE" }Signal handlers
handle_reload() { echo "$(date): Reload signal received" >> "$LOG_FILE" stop_service sleep 2 start_service }handle_shutdown() { echo "$(date): Shutdown signal received" >> "$LOG_FILE" cleanup_network exit 0 }
Set traps
trap cleanup_network EXIT trap handle_shutdown INT TERM trap handle_reload HUPMain execution
echo "$(date): Service manager starting" >> "$LOG_FILE"if start_service; then
echo "Service is running. Send SIGHUP to reload, SIGTERM/SIGINT to stop."
# Keep the script running
while kill -0 "$SERVICE_PID" 2>/dev/null; do
sleep 5
done
echo "$(date): Service process ended unexpectedly" >> "$LOG_FILE"
else
echo "$(date): Failed to start service" >> "$LOG_FILE"
exit 1
fi
`
Troubleshooting Common Issues
Issue 1: Trap Not Executing
Problem: Trap commands are not being executed when signals are received.
Common Causes and Solutions:
| Cause | Solution | Example |
|-------|----------|---------|
| Incorrect signal name | Use correct signal names | trap 'cleanup' INT not trap 'cleanup' SIGINT2 |
| Trap set after signal received | Set traps early in script | Move trap commands to top of script |
| Script exits before trap setup | Add trap immediately after shebang | See example below |
`bash
#!/bin/bash
Correct: Set trap early
trap 'echo "Cleanup"; exit' INT TERMWrong: Setting trap after potential exit points
some_command_that_might_fail
trap 'echo "Cleanup"; exit' INT TERM
`Issue 2: Infinite Loops in Trap Functions
Problem: Trap function calls itself or triggers the same signal repeatedly.
`bash
Problematic code
cleanup() { echo "Cleaning up..." exit 1 # This triggers EXIT trap again } trap cleanup EXIT INTSolution: Disable trap before exit
cleanup() { trap - EXIT INT # Disable traps first echo "Cleaning up..." exit 1 } trap cleanup EXIT INT`Issue 3: Resource Cleanup Failures
Problem: Cleanup operations fail, leaving resources in inconsistent state.
`bash
Robust cleanup with error handling
cleanup() { local exit_code=$? # Disable trap to prevent recursion trap - EXIT INT TERM # Cleanup with error suppression and logging { # File cleanup if [ -n "$TEMP_DIR" ] && [ -d "$TEMP_DIR" ]; then rm -rf "$TEMP_DIR" || echo "Warning: Failed to remove $TEMP_DIR" >&2 fi # Process cleanup if [ -n "$BACKGROUND_PID" ]; then kill -TERM "$BACKGROUND_PID" 2>/dev/null || true sleep 1 kill -KILL "$BACKGROUND_PID" 2>/dev/null || true fi # Network cleanup if [ -n "$SERVICE_PORT" ]; then fuser -k "$SERVICE_PORT/tcp" 2>/dev/null || true fi } || { echo "Some cleanup operations failed" >&2 } exit $exit_code }`Issue 4: Debugging Trap Execution
Problem: Difficulty determining if and when traps are being executed.
`bash
Debug-enabled trap function
debug_cleanup() { local exit_code=$? local signal=${1:-"unknown"} echo "DEBUG: Trap executed - Signal: $signal, Exit Code: $exit_code, PID: $" >&2 echo "DEBUG: Call stack:" >&2 # Print call stack local frame=0 while caller $frame >&2; do ((frame++)) done # Actual cleanup perform_cleanup exit $exit_code }Set debug traps
trap 'debug_cleanup EXIT' EXIT trap 'debug_cleanup INT' INT trap 'debug_cleanup TERM' TERM`Testing Trap Functionality
`bash
#!/bin/bash
trap_test.sh - Test script for trap functionality
test_cleanup() { echo "Test cleanup executed at $(date)" echo "Process ID: $" echo "Exit code: $?" # Verify cleanup actions if [ -f "/tmp/test_file_$" ]; then rm -f "/tmp/test_file_$" echo "Test file removed successfully" fi }
Set trap
trap test_cleanup EXIT INT TERMCreate test resources
touch "/tmp/test_file_$" echo "Test file created: /tmp/test_file_$"Test scenarios
echo "Testing trap functionality..." echo "1. Normal exit - let script complete" echo "2. Interrupt - press Ctrl+C" echo "3. Terminate - send SIGTERM from another terminal: kill -TERM $"Wait for testing
sleep 30echo "Script completed normally"
`
This comprehensive guide covers the essential aspects of using the trap command in Bash scripts for effective signal handling and resource management. The examples and best practices provided should enable robust script development with proper cleanup and error handling mechanisms.