CloudOpsGuide
fundamentals

Bash Scripting for DevOps: Patterns That Survive Production

Beginner
12 minutes
October 2026
CloudOpsGuide Team

Bash Scripting for DevOps: Patterns That Survive Production

Bash is the glue language of DevOps — glue scripts, cron jobs, CI steps, container entrypoints. You don't need to be a shell wizard; you need reliable patterns and an allergy to sharp edges. Here's the working set.

Table of Contents

The prologue every script needs

#!/usr/bin/env bash
set -euo pipefail
  • -e — exit on first error. Without it, failures silently cascade.
  • -u — error on unset variables instead of expanding to empty string.
  • pipefail — a failing command inside a pipe fails the pipeline (not just the last command).

Skip this line and debugging becomes archaeology.

Variables, arguments, quoting

ENV="${1:-dev}"                    # arg 1 or default
IMAGE_TAG="${GIT_SHA:-latest}"
readonly CONFIG_DIR="/etc/myapp"   # immutable

# quote everything, always:
cp "$src" "$dest"                  # handles spaces safely
# ${var:-default}   — fallback if unset
# ${var:?error}     — hard fail if unset (great for required config)

The unquoted $var splitting on whitespace has caused more production incidents than any other shell feature. Quote it.

Conditionals and loops

if [[ -f /etc/app.conf ]]; then
  echo "config found"
elif [[ "$ENV" == "prod" ]]; then
  echo "extra care"
fi

for host in web1 web2 web3; do
  ssh "$host" 'systemctl restart app'
done

while IFS= read -r line; do
  echo "$line"
done < hosts.txt

Use [[ ]] (not [ ]) — it handles strings, patterns, and && safely.

Command substitution and checks

today=$(date +%F)
count=$(kubectl get pods --no-headers | wc -l)

if command -v docker >/dev/null; then
  echo "docker installed"
fi

# exit codes
if curl -fsS http://localhost:8080/health; then
  echo "healthy"
else
  echo "down" >&2; exit 1
fi

Functions and error handling

log() { echo "[$(date +%T)] $*" ; }
die() { log "ERROR: $*" >&2; exit 1; }

deploy() {
  local env="$1"      # always declare locals
  [[ -n "$env" ]] || die "env required"
  log "deploying to $env"
}

# trap cleanup on any exit
tmp=$(mktemp -d)
trap 'rm -rf "$tmp"' EXIT

The useful one-liners

ps aux | awk '$3 > 50 {print $2, $11}'     # cpu hogs
find /var/log -name "*.log" -mtime +30 -delete
grep -rl "TODO" src/ | wc -l
tar czf backup-$(date +%F).tar.gz /etc/app

Tools that make you better

  • shellcheck — lint your scripts; catches the footguns (shellcheck script.sh, or in CI).
  • shfmt — consistent formatting.
  • set -x — trace mode; prints every command before running. Invaluable for debugging CI.

Rule of thumb: when a script grows past ~100 lines, needs real data structures, or has error paths you can't express cleanly — reach for Python or Go. Bash is glue, not an application platform.

Related Articles


Last Updated: October 2026 Author: CloudOpsGuide Team Difficulty: Beginner Estimated Reading Time: 12 minutes