Error handling and debugging techniques in Shell scripts are crucial for writing robust scripts.
Error Handling
Exit Status Codes
bash# Check command execution status command if [ $? -eq 0 ]; then echo "Command succeeded" else echo "Command failed with exit code $?" fi # Use && and || for conditional execution command1 && command2 # command2 executes only if command1 succeeds command1 || command2 # command2 executes only if command1 fails
set Command Options
bash# set -e: Exit immediately if any command fails set -e command1 command2 # Won't execute if command1 fails # set -u: Error when using undefined variables set -u echo $undefined_var # Error and exit # set -o pipefail: Return failure status if any command in pipe fails set -o pipefail command1 | command2 # Entire pipe fails if command1 fails # Combine options set -euo pipefail
Error Handling Functions
bash# Error handling function error_exit() { echo "Error: $1" >&2 exit 1 } # Check if file exists check_file() { if [ ! -f "$1" ]; then error_exit "File $1 not found" fi } # Use function check_file "config.txt"
trap Command
bash# Catch exit signals cleanup() { echo "Cleaning up..." rm -f /tmp/tempfile exit } # Catch EXIT signal trap cleanup EXIT # Catch INT signal (Ctrl+C) trap cleanup INT # Catch multiple signals trap cleanup EXIT INT TERM
Debugging Techniques
Debug Mode
bash# Enable debug mode set -x # Print each command set -v # Print input lines # Disable debug mode set +x set +v # Combine usage set -xv command1 command2 set +xv
Debug Output
bash# Use echo for debug output echo "Debug: variable = $variable" # Use printf for formatted output printf "Debug: %s = %s\n" "variable" "$variable" # Use >&2 to output to stderr echo "Debug info" >&2
Debug Functions
bash# Debug function debug() { if [ "$DEBUG" = "true" ]; then echo "[DEBUG] $*" >&2 fi } # Use debug function DEBUG=true debug "Processing file: $filename"
Variable Tracing
bash# Display variable values echo "var1 = $var1" echo "var2 = $var2" # Use declare to show variable information declare -p var1 declare -p var2 # Display all variables declare -p # Display all functions declare -f
Practical Application Examples
Robust Script Template
bash#!/bin/bash # Set error handling set -euo pipefail # Define error handling function error_exit() { echo "Error: $1" >&2 exit 1 } # Define cleanup function cleanup() { echo "Cleaning up..." [ -n "$tempfile" ] && rm -f "$tempfile" } # Set traps trap cleanup EXIT INT TERM # Define debug function debug() { if [ "${DEBUG:-false}" = "true" ]; then echo "[DEBUG] $*" >&2 fi } # Main function main() { debug "Starting script" # Check parameters if [ $# -lt 1 ]; then error_exit "Usage: $0 <filename>" fi local filename="$1" debug "Processing file: $filename" # Check file if [ ! -f "$filename" ]; then error_exit "File not found: $filename" fi # Create temp file tempfile=$(mktemp) || error_exit "Failed to create temp file" debug "Created temp file: $tempfile" # Process file cp "$filename" "$tempfile" # Processing logic... debug "Script completed successfully" } # Execute main function main "$@"
File Operations with Error Checking
bash#!/bin/bash # Safe file copy safe_copy() { local src="$1" local dst="$2" # Check source file if [ ! -f "$src" ]; then echo "Error: Source file not found: $src" >&2 return 1 fi # Check destination directory local dst_dir=$(dirname "$dst") if [ ! -d "$dst_dir" ]; then echo "Error: Destination directory not found: $dst_dir" >&2 return 1 fi # Check write permission if [ ! -w "$dst_dir" ]; then echo "Error: No write permission for: $dst_dir" >&2 return 1 fi # Execute copy if ! cp "$src" "$dst"; then echo "Error: Failed to copy $src to $dst" >&2 return 1 fi echo "Successfully copied $src to $dst" return 0 } # Use function safe_copy "source.txt" "destination.txt" || exit 1
Operations with Retry
bash#!/bin/bash # Function with retry retry_command() { local max_attempts="$1" shift local command=("$@") local attempt=1 while [ $attempt -le $max_attempts ]; do echo "Attempt $attempt of $max_attempts" if "${command[@]}"; then echo "Command succeeded" return 0 fi attempt=$((attempt + 1)) sleep 2 done echo "Command failed after $max_attempts attempts" >&2 return 1 } # Use function retry_command 3 curl -s http://example.com || exit 1
Logging
bash#!/bin/bash # Log function log() { local level="$1" shift local message="$*" local timestamp=$(date '+%Y-%m-%d %H:%M:%S') echo "[$timestamp] [$level] $message" | tee -a script.log } log_info() { log "INFO" "$@" } log_error() { log "ERROR" "$@" } log_debug() { if [ "${DEBUG:-false}" = "true" ]; then log "DEBUG" "$@" fi } # Use log functions log_info "Script started" log_debug "Debug information" log_error "An error occurred"
Debugging Best Practices
- Use set -euo pipefail: Improve script robustness
- Use trap for cleanup: Ensure resources are properly released
- Use debug functions: Easy to control debug output
- Log events: Facilitate problem tracking and debugging
- Check command status: Use $? or if statements
- Use meaningful error messages: Help quickly locate problems
- Test edge cases: Ensure script works in various scenarios
- Use variable defaults:
${VAR:-default}prevents undefined variable errors