See a common scenario
Suppose you are developing a data backup script. This script needs to perform the following operations:
- Create a temporary working directory
- Copy the data to a temporary directory
- Compression packaging
- Clean up temporary files
#!/bin/bash WORK_DIR="/tmp/backup_$(date +%Y%m%d)" echo "Start backup..." mkdir -p "$WORK_DIR" echo "Create a temporary directory: $WORK_DIR" echo "Copy the file..." cp -r /path/to/data "$WORK_DIR/" sleep 5 # Simulation time-consuming operation echo "Compression packaging..." tar -czf "$WORK_DIR" sleep 3 # Simulation time-consuming operation echo "Clean up temporary files..." rm -rf "$WORK_DIR" echo "Backup is complete!"
What if I interrupt the script!
When we run this script, what happens if we press Ctrl+C to interrupt the operation during execution?
Temporary Directory$WORK_DIR
Will be left in the system because the cleaning step is not performed. Over time, these uncleaned temporary files will occupy a lot of disk space.
Improve programs with trap command
At this time,trap
The order comes in handy.trap
A specific signal can be captured and corresponding processing functions can be performed. SIGINT (usually triggered by Ctrl+C) is one of the most common signals.
First, we define an interrupt handling function:
on_interrupt() { echo -e "\nThe program was interrupted!" echo "Clean temporary files..." rm -rf "$WORK_DIR" exit 1 }
Then, use it at the beginning of the scripttrap
Set up signal processing:
trap on_interrupt SIGINT
The complete improved script is as follows:
#!/bin/bash WORK_DIR="/tmp/backup_$(date +%Y%m%d)" # Define interrupt handling functionon_interrupt() { echo -e "\nThe program was interrupted!" echo "Clean temporary files..." rm -rf "$WORK_DIR" exit 1 } # Setting up traptrap on_interrupt SIGINT echo "Start backup..." mkdir -p "$WORK_DIR" echo "Create temporary directory: $WORK_DIR" echo "Copy the file..." cp -r /path/to/data "$WORK_DIR/" sleep 5 # Simulation time-consuming operation echo "Compression Packaging..." tar -czf "$WORK_DIR" sleep 3 # Simulation time-consuming operation echo "Clean temporary files..." rm -rf "$WORK_DIR" echo "Backup is complete!"
trap command description
trap
The basic syntax of a command is:
trap command signal
in:
-
command
Can be a function name or a direct command -
signal
It is the signal name to be captured, such as SIGINT, SIGTERM, etc.
Common signals include:
- SIGINT (2): User presses Ctrl+C
- SIGTERM (15): Termination signal
- EXIT: When the script exits
You can also capture multiple signals at the same time:
trap on_interrupt SIGINT SIGTERM
By usingtrap
Commands andon_interrupt
Function, we implement:
- Handle program interrupts gracefully
- Ensure temporary resources are properly cleaned
- Provide friendly user tips
This pattern is not only suitable for backup scripts, but also in any script that requires resource cleaning, such as:
- Temporary file processing
- Database connection cleaning
- Lock file deletion
- Process cleaning
Extensions: Advanced applications of trap commands
Multi-signal processing
Sometimes we need to process different signals differently. For example, in a data processing script:
#!/bin/bash # Define variablesDATA_FILE="" TEMP_FILE="" LOG_FILE="" # Process Ctrl+Con_interrupt() { echo -e "\nReceived SIGINT, is closing gracefully..." cleanup exit 1 } # Handle SIGTERMon_terminate() { echo -e "\nReceived SIGTERM, save the progress and exit..." save_progress cleanup exit 1 } # Process normal exiton_exit() { echo "The program ends normally, clean up..." cleanup } # Clean up functionscleanup() { rm -f "$TEMP_FILE" echo "Cleaning Completed" } # Save progresssave_progress() { echo "Save the current progress to $LOG_FILE" echo "Progress saved at $(date)" >> "$LOG_FILE" } # Set up multiple signal processingtrap on_interrupt SIGINT trap on_terminate SIGTERM trap on_exit EXIT # Main Programecho "Start the data..." while true; do echo "Processing..." sleep 1 done
Temporarily disable and restore signal processing
Sometimes we need to temporarily disable signal processing, such as when performing critical operations:
#!/bin/bash critical_operation() { # Temporarily disable Ctrl+C trap '' SIGINT echo "Perform a critical operation, pressing Ctrl+C during this period is invalid..." sleep 5 # Recover signal processing trap on_interrupt SIGINT echo "Critical operation is completed, normal signal processing is restored" } on_interrupt() { echo -e "\nThe operation was interrupted!" exit 1 } trap on_interrupt SIGINT echo "Start execution..." critical_operation echo "Continue other operations..."
DEBUG signal and debug processing
DEBUG is not an interrupt signal, but a special trap event of Bash. It is triggered before each command is executed and is mainly used for debugging purposes. Let's look at a more practical example:
#!/bin/bash # Used to control whether the DEBUG trap is triggered in the error handling functionIN_ERROR_HANDLER=0 # Define debug processing functionson_debug() { # If in the error handling function, skip debug output if ((IN_ERROR_HANDLER)); then return fi # $1 is the line number, $BASH_COMMAND is the command to be executed echo "[DEBUG] Line $1: Prepare to execute -> $BASH_COMMAND" } # Error handling functionon_error() { local err=$? # Save the error code immediately local line=$1 local cmd=$2 # Set flags to prevent DEBUG trap from triggering in error handling IN_ERROR_HANDLER=1 echo "[ERROR] line $line failed to execute" echo "Command: $cmd" echo "Error code: $err" # Reset flag IN_ERROR_HANDLER=0 } # Enable debug trackingenable_debug() { # Enable ERR trap set -E # -T option can display function call trace set -T # Set DEBUG trap and pass in line number parameters trap 'on_debug ${LINENO}' DEBUG trap 'on_error ${LINENO} "$BASH_COMMAND"' ERR } # Turn off debug trackingdisable_debug() { trap - DEBUG trap - ERR set +E set +T } # Control whether to enable debugging through environment variablesif [[ "${ENABLE_DEBUG}" == "true" ]]; then enable_debug fi # Test functiontest_function() { echo "Execute test function" local result=$((2 + 2)) echo "Calculation result: $result" # Deliberately create an error ls /nonexistent_directory } # Main Programecho "Start execution..." test_function echo "Try to access a file that does not exist..." cat nonexistent_file.txt
How to use:
# Normal execution./ # Turn on debug mode executionENABLE_DEBUG=true ./
Normal mode output:
Start executing...
Execute test functions
Calculation result: 4
ls: cannot access '/nonexistent_directory': No such file or directory
Try to access a file that does not exist...
cat: nonexistent_file.txt: No such file or directory
DEBUG mode output:
[DEBUG] Line 41: Prepare to execute -> trap 'on_error ${LINENO} "$BASH_COMMAND"' ERR
[DEBUG] Line 67: Prepare to execute -> echo "Start execution..."
Start executing...
[DEBUG] Line 68: Prepare to execute -> test_function
[DEBUG] Line 58: Prepare to execute -> test_function
[DEBUG] Line 59: Preparing to execute -> echo "Execute test function"
Execute test functions
[DEBUG] Line 60: Prepare to execute -> local result=$((2 + 2))
[DEBUG] Line 61: Prepare to execute -> echo "Calculation result: $result"
Calculation result: 4
[DEBUG] Line 63: Prepare to execute -> ls /noneexistent_directory
ls: cannot access '/nonexistent_directory': No such file or directory
[DEBUG] Line 63: Prepare to execute -> ls /noneexistent_directory
[DEBUG] Line 17: Prepare to execute -> ls /noneexistent_directory
[DEBUG] Line 18: Prepare to execute -> local err=$?
[DEBUG] Line 19: Prepare to execute -> local line=$1
[DEBUG] Line 20: Prepare to execute -> local cmd=$2
[DEBUG] Line 23: Prepare to execute -> IN_ERROR_HANDLER=1
[ERROR] Line 63 Execution failed
Command: ls /noneexistent_directory
Error code: 2
[DEBUG] Line 68: Prepare to execute -> ls /noneexistent_directory
[DEBUG] Line 17: Prepare to execute -> ls /noneexistent_directory
[DEBUG] Line 18: Prepare to execute -> local err=$?
[DEBUG] Line 19: Prepare to execute -> local line=$1
[DEBUG] Line 20: Prepare to execute -> local cmd=$2
[DEBUG] Line 23: Prepare to execute -> IN_ERROR_HANDLER=1
[ERROR] Line 68 Execution failed
Command: ls /noneexistent_directory
Error code: 2
[DEBUG] Line 69: Prepare to execute -> echo "Try to access a non-existent file..."
Try to access a file that does not exist...
[DEBUG] Line 70: Prepare to execute -> cat nonexistent_file.txt
cat: nonexistent_file.txt: No such file or directory
[DEBUG] Line 70: Prepare to execute -> cat nonexistent_file.txt
[DEBUG] Line 17: Prepare to execute -> cat nonexistent_file.txt
[DEBUG] Line 18: Prepare to execute -> local err=$?
[DEBUG] Line 19: Prepare to execute -> local line=$1
[DEBUG] Line 20: Prepare to execute -> local cmd=$2
[DEBUG] Line 23: Prepare to execute -> IN_ERROR_HANDLER=1
[ERROR] Line 70 Execution failed
Command: cat nonexistent_file.txt
Error code: 1
File lock mechanism trap vs flock
Let's compare the lock mechanisms of trap and flock:
File locks using trap
#!/bin/bash LOCK_FILE="/tmp/" PID_FILE="/tmp/" cleanup() { rm -f "$LOCK_FILE" "$PID_FILE" echo "Clean lock files and PID files" } get_lock() { if [ -e "$LOCK_FILE" ]; then local pid pid=$(cat "$PID_FILE" 2>/dev/null) if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then echo "Another example(PID: $pid)Running" exit 1 fi # If the process does not exist, clean up the old lock cleanup fi echo $$ > "$PID_FILE" touch "$LOCK_FILE" trap cleanup EXIT }
Implementation using flock:
#!/bin/bash LOCK_FILE="/tmp/" ( # Get the file lock and wait for up to 5 seconds flock -w 5 200 || { echo "Cannot acquire lock, another instance is running"; exit 1; } echo "Get the lock, start executing..." sleep 10 echo "Execution Completed" ) 200>"$LOCK_FILE"
Comparative Analysis
-
reliability
- flock is more reliable, it uses kernel-level file locks
- The trap method may leave orphaned lock files in extreme cases (such as system crashes)
-
Use scenarios
- flock is suitable for strict production environments
- The trap method is suitable for simple scripting and development environments
-
Recommended choice
- Flock is recommended because it:
- Automatic process termination
- Support timeout settings
- Provides blocking and non-blocking modes
- More reliable
- Flock is recommended because it:
Transaction implementation
#!/bin/bash # Status variablesTRANSACTION_ACTIVE=false # Dynamically change signal processingupdate_signal_handler() { if $TRANSACTION_ACTIVE; then # Transaction is in progress, set interrupt processing to prompt and end trap 'echo "The transaction is in progress and has been forcibly interrupted..."; cleanup; exit 1' SIGINT else # Non-transaction state, can exit safely trap 'echo "Exit normally..."; exit 0' SIGINT fi } # Clean up functionscleanup() { echo "Perform a cleanup operation..." # Add the actual cleaning code here} # Simulate transactionsstart_transaction() { TRANSACTION_ACTIVE=true update_signal_handler echo "Transaction Begins" # Simulate transaction operations echo "Execute transaction steps 1/3" sleep 2 echo "Execute transaction step 2/3" sleep 2 echo "Execute transaction step 3/3" sleep 2 TRANSACTION_ACTIVE=false update_signal_handler echo "Transaction Completed" } # Set initial signal processingupdate_signal_handler # Main program execution processecho "Start execution..." start_transaction echo "Continue other operations..."
Execution process description:
-
Script Start
-
TRANSACTION_ACTIVE
The initial value isfalse
- First call
update_signal_handler
, set normal interrupt processing
-
-
implement
start_transaction
- set up
TRANSACTION_ACTIVE
fortrue
- Update signal processing to transaction protection mode
- Perform transaction operations
- When finished, set up
TRANSACTION_ACTIVE
forfalse
- Restore normal signal processing
- set up
-
Signal processing behavior
- SIGINT is received during the transaction: display an interrupt message, perform a cleanup, and then exit
- Non-transaction state received SIGINT: Direct secure exit
With these advanced usages, we can build more robust and reliable shell scripts. Whether it is handling unexpected interrupts, implementing lock mechanisms, or performing debugging,trap
They are all powerful tools.
The above is the detailed content of Linux shell using trap commands to handle program interrupts elegantly. For more information about shell trap handler interrupts, please pay attention to my other related articles!