Skip to main content
ScriptingBeginner

Shell Scripting for DevOps: Essential Bash Automation

Learn essential Bash scripting for DevOps — variables, conditionals, loops, functions, error handling, cron jobs, and practical one-liners.

N
Neeraj Jha
·Updated September 11, 2026·5 min read
Shell Scripting for DevOps: Essential Bash Automation

Bash is the lingua franca of server automation. Every DevOps engineer needs to write scripts that deploy code, rotate logs, check health endpoints, and automate repetitive tasks. This guide covers the building blocks.

Script Basics

Always start with a shebang and enable strict mode:

bash
#!/usr/bin/env bash
set -euo pipefail

# -e: exit on error
# -u: treat unset variables as errors
# -o pipefail: catch errors in piped commands

Make scripts executable:

bash
chmod +x deploy.sh
./deploy.sh

Variables and Arguments

bash
# Variables
APP_NAME="my-api"
DEPLOY_DIR="/opt/${APP_NAME}"
VERSION=${1:-"latest"}    # First argument, default "latest"

echo "Deploying ${APP_NAME} version ${VERSION} to ${DEPLOY_DIR}"

# Special variables
echo "Script name: $0"
echo "Argument count: $#"
echo "All arguments: $@"
echo "Last exit code: $?"
echo "Process ID: $$"

Conditionals

bash
# File tests
if [[ -f /etc/nginx/nginx.conf ]]; then
  echo "Nginx config exists"
fi

if [[ -d /var/log/app ]]; then
  echo "Log directory exists"
fi

# String comparison
if [[ "$ENV" == "production" ]]; then
  echo "Running in production mode"
fi

# Numeric comparison
DISK_USAGE=$(df / --output=pcent | tail -1 | tr -d ' %')
if (( DISK_USAGE > 80 )); then
  echo "WARNING: Disk usage is ${DISK_USAGE}%"
fi

# Command success
if docker ps | grep -q "my-container"; then
  echo "Container is running"
else
  echo "Container is not running, starting..."
  docker start my-container
fi

Loops

bash
# Iterate over servers
SERVERS=("web1.example.com" "web2.example.com" "web3.example.com")

for server in "${SERVERS[@]}"; do
  echo "Deploying to ${server}..."
  ssh deploy@"${server}" "cd /opt/app && git pull && docker compose up -d"
done

# Retry loop with backoff
MAX_RETRIES=5
for i in $(seq 1 $MAX_RETRIES); do
  if curl -sf http://localhost:3000/health > /dev/null; then
    echo "Health check passed"
    break
  fi
  echo "Attempt $i/$MAX_RETRIES failed, retrying in ${i}s..."
  sleep "$i"
done

Functions

bash
log() {
  local level="$1"
  shift
  echo "[$(date '+%Y-%m-%d %H:%M:%S')] [${level}] $*"
}

check_dependency() {
  if ! command -v "$1" &> /dev/null; then
    log "ERROR" "$1 is required but not installed"
    exit 1
  fi
}

# Usage
check_dependency docker
check_dependency git
log "INFO" "All dependencies satisfied"

Error Handling

bash
# Trap errors and clean up
cleanup() {
  log "INFO" "Cleaning up temporary files..."
  rm -rf "${TEMP_DIR}"
}
trap cleanup EXIT

TEMP_DIR=$(mktemp -d)
log "INFO" "Working in ${TEMP_DIR}"

# Custom error handler
on_error() {
  log "ERROR" "Script failed at line $1"
  # Send notification
  curl -s -X POST "https://hooks.slack.com/..." \
    -d '{"text":"Deploy script failed at line '"$1"'"}'
}
trap 'on_error $LINENO' ERR

Cron Jobs

Automate script execution with cron:

bash
# Edit crontab
crontab -e

# Run backup every day at 2 AM
0 2 * * * /opt/scripts/backup.sh >> /var/log/backup.log 2>&1

# Health check every 5 minutes
*/5 * * * * /opt/scripts/healthcheck.sh

# Log rotation every Sunday at midnight
0 0 * * 0 /opt/scripts/rotate-logs.sh

Useful One-Liners

bash
# Watch Docker container resource usage
watch docker stats --no-stream

# Find files larger than 100MB
find / -type f -size +100M -exec ls -lh {} + 2>/dev/null

# Tail multiple log files simultaneously
tail -f /var/log/nginx/access.log /var/log/app/app.log

# Quick HTTP server for file sharing
python3 -m http.server 8080

# Generate a random password
openssl rand -base64 32

# Check SSL certificate expiry
echo | openssl s_client -connect example.com:443 2>/dev/null | openssl x509 -noout -enddate

# Kill all processes matching a pattern
pkill -f "node server.js"

# Parallel SSH to multiple servers
parallel-ssh -i -h servers.txt "uptime"

Script Template

bash
#!/usr/bin/env bash
set -euo pipefail

readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly SCRIPT_NAME="$(basename "$0")"

log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"; }

usage() {
  cat <<EOF
Usage: ${SCRIPT_NAME} [options]

Options:
  -e, --env ENV       Target environment (staging|production)
  -v, --version VER   Version to deploy
  -h, --help          Show this help
EOF
}

main() {
  local env="staging"
  local version="latest"

  while [[ $# -gt 0 ]]; do
    case "$1" in
      -e|--env) env="$2"; shift 2 ;;
      -v|--version) version="$2"; shift 2 ;;
      -h|--help) usage; exit 0 ;;
      *) log "Unknown option: $1"; usage; exit 1 ;;
    esac
  done

  log "Deploying version ${version} to ${env}"
  # Your logic here
}

main "$@"

Master these patterns and you can automate virtually any server task. Start simple, add error handling, and evolve scripts into roles or modules as complexity grows.

Tagged with

Enjoyed this article?

Get more DevOps insights delivered to your inbox.

Get new posts by email

Subscribe to get an email when a new blog post is published. Skip anytime.

No spam, unsubscribe anytime.

N

Written by

Neeraj Jha

Platform administrator and lead writer.

View all posts

Discussion

0 comments

Sign in to join the conversation.

Be the first to comment

Start a conversation about this post

Share: