Shell Scripting Bash Conditionals Basics May 2026

Shell Scripting Conditionals

Master if/elif/else, the case statement, nested conditions, one-liners, and every conditional pattern used in production bash scripts — from simple environment checks to multi-branch deployment logic.

Conditional logic is the backbone of any non-trivial script. Bash offers if/elif/else for readable multi-branch logic and case for clean pattern matching. Knowing when to use each — and how to write them concisely — is essential for production scripts.

BASH
#!/usr/bin/env bash
# Basic if/elif/else structure

score=75

if (( score >= 90 )); then
  echo "Grade: A"
elif (( score >= 75 )); then
  echo "Grade: B"
elif (( score >= 60 )); then
  echo "Grade: C"
else
  echo "Grade: F"
fi

# ── String conditions ─────────────────────────────────────
env="production"

if [[ "${env}" == "production" ]]; then
  echo "Running in PRODUCTION — extra caution!"
elif [[ "${env}" == "staging" ]]; then
  echo "Staging environment"
else
  echo "Development / unknown: ${env}"
fi

# ── File conditions ───────────────────────────────────────
config="/etc/myapp/config.env"

if [[ ! -f "${config}" ]]; then
  echo "ERROR: Config file missing: ${config}" >&2
  exit 1
elif [[ ! -r "${config}" ]]; then
  echo "ERROR: Config file not readable: ${config}" >&2
  exit 1
else
  source "${config}"
  echo "Config loaded successfully"
fi

case is cleaner than long if/elif chains when you're matching a variable against multiple fixed patterns. It supports glob patterns, multiple patterns per branch, and fall-through alternatives.

BASH
# ── Basic case ────────────────────────────────────────────
action="${1:-start}"

case "${action}" in
  start)
    echo "Starting service..."
    systemctl start myapp
    ;;
  stop)
    echo "Stopping service..."
    systemctl stop myapp
    ;;
  restart)
    echo "Restarting service..."
    systemctl restart myapp
    ;;
  status)
    systemctl status myapp
    ;;
  *)
    echo "Usage: $0 {start|stop|restart|status}" >&2
    exit 1
    ;;
esac

# ── Multiple patterns per branch ──────────────────────────
ext="${file##*.}"

case "${ext}" in
  jpg|jpeg|png|gif|webp)
    echo "Image file"
    ;;
  mp4|mkv|avi|mov)
    echo "Video file"
    ;;
  sh|bash|zsh)
    echo "Shell script"
    ;;
  csv|tsv|xlsx)
    echo "Spreadsheet"
    ;;
  *)
    echo "Unknown type: ${ext}"
    ;;
esac

# ── Glob patterns in case ─────────────────────────────────
version="5.7.42"

case "${version}" in
  8.*)   echo "MySQL 8.x" ;;
  5.7.*) echo "MySQL 5.7" ;;
  5.6.*) echo "MySQL 5.6 — EOL!" ;;
  *)     echo "Unknown MySQL version" ;;
esac
Key Matched branch Error / default Warning branch
bash — conditionals demo
vriddh@prod-01:~/scripts$./service.sh start
Starting service...
vriddh@prod-01:~/scripts$./service.sh reload
Usage: service.sh {start|stop|restart|status}
vriddh@prod-01:~/scripts$version=5.7.42 bash version_check.sh
MySQL 5.7
vriddh@prod-01:~/scripts$version=5.6.50 bash version_check.sh
MySQL 5.6 — EOL!
BASH
# ── && || one-liners ──────────────────────────────────────
[[ -d "/app" ]] && echo "app dir exists"
[[ -f "/app/pid" ]] || echo "app not running"

# Guard — exit immediately on failure
[[ "${EUID}" -eq 0 ]] || { echo "Need root" >&2; exit 1; }
[[ -n "${DB_PASS}" ]] || { echo "DB_PASS not set" >&2; exit 1; }

# ── Ternary simulation ────────────────────────────────────
status=$( [[ "${1}" == "ok" ]] && echo "UP" || echo "DOWN" )
echo "Service: ${status}"

# ── if on one line ────────────────────────────────────────
if [[ -f "/etc/passwd" ]]; then echo "exists"; fi

# ── Conditional assignment ────────────────────────────────
log_dir="/var/log/myapp"
[[ -d "${log_dir}" ]] || mkdir -p "${log_dir}"

# ── Check command availability ────────────────────────────
command -v jq >/dev/null 2>&1 || { echo "jq not installed"; exit 1; }
command -v curl >/dev/null 2>&1 && echo "curl available"
BASH
#!/usr/bin/env bash
# db_check.sh — Multi-level environment and DB validation

ENV="${APP_ENV:-development}"
DB_HOST="${DB_HOST:-localhost}"
DB_PORT="${DB_PORT:-3306}"

if [[ "${ENV}" == "production" ]]; then
  echo "[PROD] Running production checks..."

  if [[ "${DB_HOST}" == "localhost" ]]; then
    echo "ERROR: Production must not use localhost DB" >&2
    exit 1
  elif ! nc -z "${DB_HOST}" "${DB_PORT}" >/dev/null 2>&1; then
    echo "ERROR: Cannot reach DB ${DB_HOST}:${DB_PORT}" >&2
    exit 1
  else
    echo "  DB reachable: ${DB_HOST}:${DB_PORT}"
  fi

elif [[ "${ENV}" == "staging" ]]; then
  echo "[STAGING] Lighter checks..."
  nc -z "${DB_HOST}" "${DB_PORT}" >/dev/null 2>&1 \
    && echo "  DB OK" \
    || echo "  WARNING: DB not reachable"

else
  echo "[DEV] Skipping DB checks for local dev"
fi

echo "Checks complete for ENV=${ENV}"
BASH
#!/usr/bin/env bash
# deploy_gate.sh — Pre-deployment validation checks

set -euo pipefail

ERRORS=0
WARNINGS=0

check() {
  local label="${1}"
  local result="${2}"   # "ok", "warn", or "fail"
  local msg="${3:-}"

  case "${result}" in
    ok)   printf "  ✔ %-30s %s\n" "${label}" "${msg}" ;;
    warn) printf "  ⚠ %-30s %s\n" "${label}" "${msg}"; (( WARNINGS++ )) ;;
    fail) printf "  ✘ %-30s %s\n" "${label}" "${msg}"; (( ERRORS++ )) ;;
  esac
}

echo "Pre-deployment checks:"

if [[ "${EUID}" -eq 0 ]]; then
  check "Running as root" "warn" "Consider using a service account"
else
  check "Running as root" "ok" "user: ${USER}"
fi

[[ -n "${APP_VERSION:-}" ]] \
  && check "APP_VERSION set" "ok" "v${APP_VERSION}" \
  || check "APP_VERSION set" "fail" "required"

[[ -f "/app/current/Makefile" ]] \
  && check "Makefile present" "ok" "" \
  || check "Makefile present" "fail" "not found"

echo "─────────────────────────────────"
echo "Errors: ${ERRORS}  Warnings: ${WARNINGS}"

if (( ERRORS > 0 )); then
  echo "Deployment blocked — fix errors first" >&2
  exit 1
fi
echo "All checks passed — proceeding"
bash — deploy_gate.sh
vriddh@prod-01:~/scripts$APP_VERSION=2.4.1 ./deploy_gate.sh
Pre-deployment checks:
✔ Running as root user: vriddh
✔ APP_VERSION set v2.4.1
✘ Makefile present not found
─────────────────────────────────
Errors: 1 Warnings: 0
Deployment blocked — fix errors first
✔ Best practices — Prefer case over long if/elif chains when matching one variable against many values. Always include a *) default in every case. Use || { echo ...; exit 1; } for compact guards. Keep nesting to a maximum of 3 levels — extract deeper logic into functions.