Skip to content

Strict_mode

Strict mode is a collection of bash options that make scripts more robust, reliable, and easier to debug. This chapter covers the recommended settings for production-quality bash scripts.


Strict mode is a set of bash options that:

  • Exit on errors (set -e)
  • Exit on undefined variables (set -u)
  • Pipeline fails on command failure (set -o pipefail)
┌────────────────────────────────────────────────────────────────┐
│ Strict Mode Benefits │
├────────────────────────────────────────────────────────────────┤
│ │
│ 1. Fail Fast - Errors caught immediately │
│ 2. No Silent Failures - Unset variables cause errors │
│ 3. Pipeline Errors - All pipeline commands must succeed │
│ 4. Easier Debugging - Clear error locations │
│ 5. More Predictable - No unexpected behavior │
│ │
└────────────────────────────────────────────────────────────────┘

# Exit immediately if command exits with non-zero status
#!/bin/bash
set -e
# This will cause script to exit
false
echo "This never runs"
# Unless handled
false || true
echo "This runs because of || true"
# Exit when trying to use undefined variables
#!/bin/bash
set -u
echo "$undefined_var" # Error!
# Unless handled
echo "${undefined_var:-default}" # Uses default
# Pipeline returns the exit status of the rightmost command
#!/bin/bash
set -euo pipefail
# Without pipefail: succeeds (last command)
# With pipefail: fails (first command fails)
false | true
echo "This runs without pipefail"

#!/usr/bin/env bash
# Strict mode
set -euo pipefail
Terminal window
# Basic strict mode
set -euo pipefail
# Short form
set -Eeuo pipefail
# With xtrace for debugging
set -euxo pipefail

#!/bin/bash
set -euo pipefail
# Using ||
command_that_might_fail || true
echo "Continues after potential failure"
# Using conditional
if command_that_might_fail; then
echo "Success"
fi
echo "Continues either way"
# Using if blocks
if ! command_that_might_fail; then
echo "Command failed, but we handled it"
fi
#!/bin/bash
set -euo pipefail
# Using default values
echo "${UNDEFINED_VAR:-default_value}"
# Using parameter expansion
: "${VAR:=value}" # Sets default if not set
# Using conditional
if [[ -n "${VAR:-}" ]]; then
echo "VAR is set: $VAR"
fi
# Checking if set
if [[ -v VAR ]]; then
echo "VAR is set"
fi

#!/usr/bin/env bash
# Put at the top of every script
set -euo pipefail
#!/bin/bash
set -euo pipefail
# Initialize variables
VALUE=""
COUNT=0
#!/bin/bash
set -euo pipefail
# Environment variables
DATABASE_URL="${DATABASE_URL:-postgresql://localhost/db}"
# Function arguments
process_file() {
local file="${1:-default.txt}"
# ...
}

#!/usr/bin/env bash
# Database backup with strict mode
set -euo pipefail
DATABASE_URL="${DATABASE_URL:-postgresql://localhost/appdb}"
BACKUP_DIR="${BACKUP_DIR:-/backups}"
log() { echo "[$(date)] $*"; }
main() {
local timestamp
timestamp=$(date +%Y%m%d_%H%M%S)
log "Starting backup"
local backup_file="${BACKUP_DIR}/db_${timestamp}.sql.gz"
# Check if backup directory exists
mkdir -p "$BACKUP_DIR"
# Run backup
if ! pg_dump "$DATABASE_URL" | gzip > "$backup_file"; then
log "Backup failed!"
return 1
fi
# Verify backup
if [[ ! -s "$backup_file" ]]; then
log "Backup file is empty!"
return 1
fi
log "Backup completed: $backup_file"
}
main "$@"
#!/usr/bin/env bash
# API client with strict mode
set -euo pipefail
API_URL="${API_URL:-https://api.example.com}"
API_KEY="${API_KEY:-}"
log() { echo "[$(date)] $*"; }
error() { echo "[$(date)] ERROR: $*" >&2; exit 1; }
# Check required variables
: "${API_KEY:?API_KEY environment variable is required}"
fetch_data() {
local endpoint="$1"
local response
response=$(curl -s -H "Authorization: Bearer $API_KEY" \
"${API_URL}/${endpoint}")
if [[ -z "$response" ]]; then
error "Empty response from API"
fi
echo "$response"
}
# Example usage
data=$(fetch_data "users")
echo "$data"

Terminal window
# Problem: grep returns 1 when no match
# This fails in strict mode!
set -euo pipefail
result=$(grep "pattern" file.txt) # Fails if no match
# Fix: handle the case
set -euo pipefail
result=$(grep "pattern" file.txt || echo "not found")
Terminal window
# Problem: command substitution changes $?
set -euo pipefail
# This might fail unexpectedly
output=$(command_that_can_fail)
# $? is now about command_substitution, not the command
# Fix: check status explicitly
command_that_can_fail || exit 1
output=$(command_that_can_fail)
Terminal window
# Problem: empty arrays
set -euo pipefail
arr=()
echo "${arr[0]}" # Error!
# Fix: check length first
arr=()
if [[ ${#arr[@]} -gt 0 ]]; then
echo "${arr[0]}"
fi

#!/bin/bash
set -euo pipefail
# Disable for specific section
set +e
command_that_might_fail
set -e
# Or
command_that_might_fail || true
#!/bin/bash
set -euo pipefail
# Add logging before failing sections
set -x
command_that_might_fail
set +x

#!/bin/bash
strict_mode() {
set -euo pipefail
}
# Use in subshell
(
strict_mode
# This subshell is in strict mode
)
# Original shell unchanged
#!/bin/bash
my_function() {
local set -u
# This function doesn't require -u
set -e
# This function requires -e
# Function code
}

In this chapter, you learned:

  • ✅ What strict mode is
  • ✅ set -e (errexit)
  • ✅ set -u (nounset)
  • ✅ set -o pipefail
  • ✅ Combined usage
  • ✅ Handling strict mode issues
  • ✅ Best practices
  • ✅ Real-world examples
  • ✅ Common pitfalls
  • ✅ Debugging tips

Continue to the next chapter to learn about Best Practices.


Previous Chapter: Debugging Techniques Next Chapter: Best Practices