Exit_status
Chapter 30: Exit Status and Error Handling
Section titled “Chapter 30: Exit Status and Error Handling”Overview
Section titled “Overview”Exit status and error handling are critical for writing robust bash scripts. Proper error handling ensures your scripts behave predictably and provide useful feedback when things go wrong - essential for DevOps automation.
Understanding Exit Status
Section titled “Understanding Exit Status”What is Exit Status?
Section titled “What is Exit Status?”Every command in Linux returns an exit status (0-255):
- 0 = Success
- Non-zero = Failure (1-255)
┌────────────────────────────────────────────────────────────────┐│ Exit Status Flow │├────────────────────────────────────────────────────────────────┤│ ││ Command runs ││ │ ││ ▼ ││ ┌─────────┐ ││ │ Returns │ ││ │ Exit │ ││ │ Code │ ││ └────┬────┘ ││ │ ││ ▼ ││ Stored in $? ││ ││ ┌──────────────────────────────────────┐ ││ │ 0 = Success │ ││ │ 1 = General error │ ││ │ 2 = Misuse of shell command │ ││ │ 126 = Command not executable │ ││ │ 127 = Command not found │ ││ │ 128+N = Signal N termination │ ││ └──────────────────────────────────────┘ ││ │└────────────────────────────────────────────────────────────────┘Checking Exit Status
Section titled “Checking Exit Status”Using $?
Section titled “Using $?”# Basic checkls /etc/passwdecho $? # 0 (success)
ls /nonexistentecho $? # 2 (failure)
# Capture exit statuscommandstatus=$?if [[ $status -ne 0 ]]; then echo "Command failed with status $status"fiIn Conditions
Section titled “In Conditions”# Using ifif ls /etc/passwd; then echo "File exists"fi
# Using ||if ! ls /etc/passwd; then echo "File does not exist"fi
# Using &&ls /etc/passwd && echo "File exists"Common Exit Codes
Section titled “Common Exit Codes”Standard Exit Codes
Section titled “Standard Exit Codes”| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | General error |
| 2 | Misuse of shell command |
| 126 | Command not executable |
| 127 | Command not found |
| 128 | Invalid exit argument |
| 130 | Script terminated (Ctrl+C) |
| 255 | Exit out of range |
Common Signals
Section titled “Common Signals”| Code | Signal |
|---|---|
| 129 | SIGHUP (1) |
| 130 | SIGINT (2) |
| 131 | SIGQUIT (3) |
| 137 | SIGKILL (9) |
| 143 | SIGTERM (15) |
Error Handling Patterns
Section titled “Error Handling Patterns”Basic Error Handling
Section titled “Basic Error Handling”#!/bin/bash
# Simple error checkcommand || { echo "Command failed"; exit 1; }
# With more detailif ! command; then echo "Command failed" exit 1fiUsing set for Error Handling
Section titled “Using set for Error Handling”#!/bin/bash
# Exit on errorset -e
# Commands that fail will cause script to exitcommand_that_might_fail
# Continue if error is expectedcommand_that_might_fail || trueAdvanced Error Handling
Section titled “Advanced Error Handling”#!/bin/bash
# Exit on error and unset variableset -eu
# Or more comprehensiveset -o errexit -o nounset -o pipefail
# Function for error handlingerror() { echo "ERROR: $*" >&2 exit 1}
# Use error functioncommand || error "command failed"Returning Exit Status
Section titled “Returning Exit Status”From Functions
Section titled “From Functions”#!/bin/bash
# Function with exit statuscheck_file() { if [[ -f "$1" ]]; then echo "File exists" return 0 else echo "File not found" return 1 fi}
# Using functioncheck_file "/etc/passwd" && echo "Found" || echo "Not found"Exit with Status
Section titled “Exit with Status”#!/bin/bash
# Exit with specific statusif [[ $# -lt 1 ]]; then echo "Usage: $0 <argument>" exit 1fi
# Exit with 0 for successexit 0
# Exit with 1 for errorexit 1Real-World Examples
Section titled “Real-World Examples”Database Backup Script
Section titled “Database Backup Script”#!/bin/bash# Database backup with error handling
set -o pipefail
DB_NAME="${DB_NAME:-appdb}"BACKUP_DIR="${BACKUP_DIR:-/backups}"
log() { echo "[$(date)] $*"; }
error() { log "ERROR: $*" >&2 exit 1}
# Check prerequisitescommand -v mysqldump >/dev/null 2>&1 || error "mysqldump not found"
# Create backuptimestamp=$(date +%Y%m%d_%H%M%S)backup_file="$BACKUP_DIR/${DB_NAME}_${timestamp}.sql.gz"
log "Starting backup of $DB_NAME"
if ! mysqldump -u root "$DB_NAME" | gzip > "$backup_file"; then error "mysqldump failed"fi
if [[ ! -s "$backup_file" ]]; then error "Backup file is empty"fi
log "Backup completed: $backup_file"exit 0API Health Check
Section titled “API Health Check”#!/bin/bash# Health check script with retry
set -euo pipefail
URL="${1:-http://localhost:8080/health}"MAX_RETRIES="${2:-3}"RETRY_DELAY="${3:-5}"
check_health() { curl -sf -o /dev/null -w "%{http_code}" "$URL" | grep -q "200"}
for attempt in $(seq 1 $MAX_RETRIES); do if check_health; then echo "Service is healthy" exit 0 fi echo "Attempt $attempt/$MAX_RETRIES failed, retrying in ${RETRY_DELAY}s..." sleep $RETRY_DELAYdone
echo "Service is unhealthy after $MAX_RETRIES attempts"exit 1Deployment Script
Section titled “Deployment Script”#!/bin/bash# Deployment script with rollback
set -euo pipefail
APP_DIR="/opt/myapp"BACKUP_DIR="/opt/backup"
log() { echo "[$(date)] $*"; }
# Create backuplog "Creating backup..."tar -czf "$BACKUP_DIR/app_$(date +%Y%m%d_%H%M%S).tar.gz" -C "$(dirname $APP_DIR)" "$(basename $APP_DIR)" || { log "Backup failed, aborting deployment" exit 1}
# Deploy new versionlog "Deploying new version..."if ! rsync -av --delete ./app/ "$APP_DIR/"; then log "Deployment failed" log "Restoring from backup..." # Restore would go here exit 1fi
# Restart servicelog "Restarting service..."if ! systemctl restart myapp; then log "Service restart failed" exit 1fi
log "Deployment completed successfully"exit 0Pipelines and Exit Status
Section titled “Pipelines and Exit Status”pipefail Option
Section titled “pipefail Option”# Without pipefailset -efalse | true # Succeeds (last command is true)
# With pipefailset -o pipefailfalse | true # Fails (first command failed)Checking Pipeline Status
Section titled “Checking Pipeline Status”# Check specific command in pipelinecommand1 | command2 | command3status=${PIPESTATUS[0]} # Status of command1echo "${PIPESTATUS[@]}" # Status of all commandsTrap for Cleanup
Section titled “Trap for Cleanup”Exit Traps
Section titled “Exit Traps”#!/bin/bash
cleanup() { local exit_status=$? echo "Cleaning up..." # Cleanup code here echo "Exit status: $exit_status" exit $exit_status}
trap cleanup EXITtrap 'echo "Interrupted"; exit 130' INT TERMTesting Exit Status
Section titled “Testing Exit Status”Conditional Operators
Section titled “Conditional Operators”# && - Run if previous succeededcommand1 && command2 && command3
# || - Run if previous failedcommand1 || fallback_command
# ; - Always runcommand1 ; command2Combining Conditions
Section titled “Combining Conditions”# Run multiple commandscommand1 && { command2 command3} || echo "Commands failed"
# Complex conditionif command1 && command2 || command3; then echo "Success"fiSummary
Section titled “Summary”In this chapter, you learned:
- ✅ What exit status is and how it works
- ✅ Checking exit status with $?
- ✅ Common exit codes and their meanings
- ✅ Basic and advanced error handling patterns
- ✅ Returning exit status from functions
- ✅ Real-world DevOps examples
- ✅ Pipeline exit status and pipefail
- ✅ Using trap for cleanup
- ✅ Testing exit status with conditions
Next Steps
Section titled “Next Steps”Continue to the next chapter to learn about Debugging Techniques.
Previous Chapter: Command Substitution Next Chapter: Debugging Techniques