Skip to content

Script Examples

Joao Palma edited this page Oct 27, 2025 · 4 revisions

Script Examples

← Back to README | Home | Troubleshooting →


Table of Contents

Basic Examples

Hello World

#!/usr/bin/env bash
### DOC
# hello - Simple greeting script
#
# Usage: dr hello [name]
#
# Examples:
#   dr hello
#   dr hello Alice
### DOC

name="${1:-World}"
echo "Hello, $name!"

Interactive Input

#!/usr/bin/env bash
### DOC
# prompt - Interactive user input example
#
# Usage: dr prompt
### DOC

read -p "What's your name? " name
read -p "What's your favorite color? " color

echo "Hello $name! $color is a great color!"

Argument Parsing

#!/usr/bin/env bash
### DOC
# args-demo - Demonstrate argument parsing
#
# Usage: dr args-demo [options] <file>
#
# Options:
#   -v, --verbose    Enable verbose output
#   -f, --force      Force operation
#   -o, --output     Output file
### DOC

VERBOSE=false
FORCE=false
OUTPUT=""

while [[ $# -gt 0 ]]; do
  case $1 in
    -v | --verbose)
      VERBOSE=true
      shift
      ;;
    -f | --force)
      FORCE=true
      shift
      ;;
    -o | --output)
      OUTPUT="$2"
      shift 2
      ;;
    -*)
      echo "Unknown option: $1"
      exit 1
      ;;
    *)
      FILE="$1"
      shift
      ;;
  esac
done

[[ "$VERBOSE" == true ]] && echo "Verbose mode enabled"
[[ "$FORCE" == true ]] && echo "Force mode enabled"
[[ -n "$OUTPUT" ]] && echo "Output file: $OUTPUT"
[[ -n "$FILE" ]] && echo "Input file: $FILE"

Git Automation

Quick Commit

#!/usr/bin/env bash
### DOC
# git/quick-commit - Fast git commit with generated message
#
# Usage: dr git/quick-commit [message]
#
# If no message provided, generates one from changes
#
# Examples:
#   dr git/quick-commit
#   dr git/quick-commit "Fix typo"
### DOC

set -euo pipefail

# Check for changes
if [[ -z $(git status --porcelain) ]]; then
  echo "No changes to commit"
  exit 0
fi

# Generate or use provided message
if [[ $# -eq 0 ]]; then
  # Auto-generate message from changes
  added=$(git diff --cached --name-only --diff-filter=A | wc -l)
  modified=$(git diff --cached --name-only --diff-filter=M | wc -l)
  deleted=$(git diff --cached --name-only --diff-filter=D | wc -l)

  message="Update: "
  [[ $added -gt 0 ]] && message+="$added added, "
  [[ $modified -gt 0 ]] && message+="$modified modified, "
  [[ $deleted -gt 0 ]] && message+="$deleted deleted"
  message=${message%, }
else
  message="$*"
fi

# Stage and commit
git add -A
git commit -m "$message"

echo "✓ Committed: $message"

Branch Cleanup

#!/usr/bin/env bash
### DOC
# git/branch-cleanup - Remove merged branches
#
# Safely removes local branches that have been merged to main/master
#
# Usage: dr git/branch-cleanup [--dry-run]
#
# Options:
#   --dry-run    Show what would be deleted
### DOC

set -euo pipefail

DRY_RUN=false
[[ "${1:-}" == "--dry-run" ]] && DRY_RUN=true

# Get default branch
DEFAULT_BRANCH=$(git symbolic-ref refs/remotes/origin/HEAD | sed 's@^refs/remotes/origin/@@')

# Get merged branches
BRANCHES=$(git branch --merged "$DEFAULT_BRANCH" | grep -v "^[* ] $DEFAULT_BRANCH$" | sed 's/^[ *]*//')

if [[ -z "$BRANCHES" ]]; then
  echo "No merged branches to clean up"
  exit 0
fi

echo "Branches merged to $DEFAULT_BRANCH:"
echo "$BRANCHES"

if [[ "$DRY_RUN" == true ]]; then
  echo ""
  echo "Run without --dry-run to delete these branches"
else
  echo ""
  read -p "Delete these branches? [y/N] " -n 1 -r
  echo
  if [[ $REPLY =~ ^[Yy]$ ]]; then
    echo "$BRANCHES" | xargs -n 1 git branch -d
    echo "✓ Deleted merged branches"
  else
    echo "Cancelled"
  fi
fi

PR Create

#!/usr/bin/env bash
### DOC
# git/pr-create - Create pull request with template
#
# Creates a PR with a standard template and opens in browser
#
# Usage: dr git/pr-create [title]
#
# Requires: gh (GitHub CLI)
### DOC

set -euo pipefail

# Check for gh
if ! command -v gh &>/dev/null; then
  echo "Error: GitHub CLI (gh) is required"
  echo "Install: https://cli.github.com/"
  exit 1
fi

# Get PR title
if [[ $# -eq 0 ]]; then
  # Generate from branch name
  branch=$(git branch --show-current)
  title=$(echo "$branch" | sed 's/[-_]/ /g' | sed 's/\b\(.\)/\u\1/g')
else
  title="$*"
fi

# Create PR body template
body=$(
  cat <<'EOF'
## Summary
Brief description of changes

## Type of Change
- [ ] Bug fix
- [ ] New feature
- [ ] Breaking change
- [ ] Documentation update

## Testing
- [ ] Tests pass locally
- [ ] Added new tests
- [ ] Updated documentation

## Checklist
- [ ] Code follows style guidelines
- [ ] Self-review completed
- [ ] Comments added where needed
EOF
)

# Create PR
gh pr create --title "$title" --body "$body" --web

Development Tools

React Component Generator

#!/usr/bin/env bash
### DOC
# dev/react-component - Generate React component boilerplate
#
# Usage: dr dev/react-component <ComponentName> [--typescript]
#
# Options:
#   --typescript    Generate TypeScript component
#
# Examples:
#   dr dev/react-component Button
#   dr dev/react-component Card --typescript
### DOC

set -euo pipefail

if [[ $# -eq 0 ]]; then
  echo "Error: Component name required"
  exit 1
fi

COMPONENT_NAME="$1"
USE_TS=false

[[ "${2:-}" == "--typescript" ]] && USE_TS=true

# File extension
EXT="jsx"
[[ "$USE_TS" == true ]] && EXT="tsx"

# Create component directory
mkdir -p "$COMPONENT_NAME"

# Component file
if [[ "$USE_TS" == true ]]; then
  cat >"$COMPONENT_NAME/$COMPONENT_NAME.$EXT" <<EOF
import React from 'react';
import './$COMPONENT_NAME.css';

interface ${COMPONENT_NAME}Props {
  // Add props here
}

export const $COMPONENT_NAME: React.FC<${COMPONENT_NAME}Props> = (props) => {
  return (
    <div className="$COMPONENT_NAME">
      <h1>$COMPONENT_NAME Component</h1>
    </div>
  );
};
EOF
else
  cat >"$COMPONENT_NAME/$COMPONENT_NAME.$EXT" <<EOF
import React from 'react';
import './$COMPONENT_NAME.css';

export const $COMPONENT_NAME = (props) => {
  return (
    <div className="$COMPONENT_NAME">
      <h1>$COMPONENT_NAME Component</h1>
    </div>
  );
};
EOF
fi

# CSS file
cat >"$COMPONENT_NAME/$COMPONENT_NAME.css" <<EOF
.$COMPONENT_NAME {
  /* Add styles here */
}
EOF

# Index file
cat >"$COMPONENT_NAME/index.$EXT" <<EOF
export { $COMPONENT_NAME } from './$COMPONENT_NAME';
EOF

# Test file
cat >"$COMPONENT_NAME/$COMPONENT_NAME.test.$EXT" <<EOF
import { render, screen } from '@testing-library/react';
import { $COMPONENT_NAME } from './$COMPONENT_NAME';

describe('$COMPONENT_NAME', () => {
  it('renders without crashing', () => {
    render(<$COMPONENT_NAME />);
    expect(screen.getByText('$COMPONENT_NAME Component')).toBeInTheDocument();
  });
});
EOF

echo "✓ Created $COMPONENT_NAME component in ./$COMPONENT_NAME/"

API Test Runner

#!/usr/bin/env bash
### DOC
# dev/api-test - Run API endpoint tests
#
# Usage: dr dev/api-test <endpoint> [--method GET|POST|PUT|DELETE]
#
# Options:
#   --method    HTTP method (default: GET)
#   --data      JSON data for POST/PUT
#   --token     Auth token
#
# Examples:
#   dr dev/api-test /users
#   dr dev/api-test /users --method POST --data '{"name":"John"}'
### DOC

set -euo pipefail

API_BASE="${API_BASE:-http://localhost:3000/api}"
METHOD="GET"
DATA=""
TOKEN="${API_TOKEN:-}"

# Parse arguments
ENDPOINT="$1"
shift

while [[ $# -gt 0 ]]; do
  case $1 in
    --method)
      METHOD="$2"
      shift 2
      ;;
    --data)
      DATA="$2"
      shift 2
      ;;
    --token)
      TOKEN="$2"
      shift 2
      ;;
    *)
      echo "Unknown option: $1"
      exit 1
      ;;
  esac
done

# Build curl command
CURL_CMD="curl -X $METHOD"
CURL_CMD+=" -H 'Content-Type: application/json'"

[[ -n "$TOKEN" ]] && CURL_CMD+=" -H 'Authorization: Bearer $TOKEN'"
[[ -n "$DATA" ]] && CURL_CMD+=" -d '$DATA'"

CURL_CMD+=" ${API_BASE}${ENDPOINT}"

# Execute and format
echo "Testing: $METHOD $ENDPOINT"
echo "Command: $CURL_CMD"
echo "---"

eval "$CURL_CMD" | jq '.' || eval "$CURL_CMD"

Deployment Scripts

Docker Deploy

#!/usr/bin/env bash
### DOC
# deploy/docker - Deploy application with Docker
#
# Usage: dr deploy/docker <environment> [--build]
#
# Arguments:
#   environment    Target environment (dev|staging|prod)
#
# Options:
#   --build        Rebuild images before deploying
#
# Examples:
#   dr deploy/docker staging
#   dr deploy/docker prod --build
### DOC

set -euo pipefail

if [[ $# -eq 0 ]]; then
  echo "Error: Environment required (dev|staging|prod)"
  exit 1
fi

ENV="$1"
BUILD=false

[[ "${2:-}" == "--build" ]] && BUILD=true

# Validate environment
case "$ENV" in
  dev | staging | prod) ;;
  *)
    echo "Error: Invalid environment: $ENV"
    exit 1
    ;;
esac

# Load environment config
ENV_FILE=".env.$ENV"
if [[ ! -f "$ENV_FILE" ]]; then
  echo "Error: Environment file not found: $ENV_FILE"
  exit 1
fi

echo "Deploying to $ENV environment..."

# Build if requested
if [[ "$BUILD" == true ]]; then
  echo "Building images..."
  docker-compose -f docker-compose.yml -f docker-compose.$ENV.yml build
fi

# Deploy
echo "Starting containers..."
docker-compose -f docker-compose.yml -f docker-compose.$ENV.yml up -d

# Health check
echo "Waiting for health check..."
sleep 5

if docker-compose ps | grep -q "healthy"; then
  echo "✓ Deployment successful!"
  docker-compose ps
else
  echo "✗ Health check failed!"
  docker-compose logs --tail=50
  exit 1
fi

Kubernetes Deploy

#!/usr/bin/env bash
### DOC
# deploy/k8s - Deploy to Kubernetes cluster
#
# Usage: dr deploy/k8s <app> <version> [--namespace prod]
#
# Arguments:
#   app         Application name
#   version     Version tag
#
# Options:
#   --namespace    Kubernetes namespace (default: default)
#   --dry-run      Show what would be deployed
#
# Examples:
#   dr deploy/k8s myapp v1.2.3
#   dr deploy/k8s myapp v1.2.3 --namespace production
### DOC

set -euo pipefail

if [[ $# -lt 2 ]]; then
  echo "Error: App name and version required"
  exit 1
fi

APP="$1"
VERSION="$2"
NAMESPACE="default"
DRY_RUN=""

shift 2

# Parse options
while [[ $# -gt 0 ]]; do
  case $1 in
    --namespace)
      NAMESPACE="$2"
      shift 2
      ;;
    --dry-run)
      DRY_RUN="--dry-run=client"
      shift
      ;;
    *)
      echo "Unknown option: $1"
      exit 1
      ;;
  esac
done

echo "Deploying $APP:$VERSION to $NAMESPACE namespace"

# Update image
kubectl set image deployment/$APP $APP=$APP:$VERSION \
  --namespace=$NAMESPACE \
  $DRY_RUN

if [[ -z "$DRY_RUN" ]]; then
  # Wait for rollout
  echo "Waiting for rollout..."
  kubectl rollout status deployment/$APP \
    --namespace=$NAMESPACE \
    --timeout=300s

  echo "✓ Deployment complete!"

  # Show status
  kubectl get pods -l app=$APP --namespace=$NAMESPACE
fi

System Utilities

Backup Script

#!/usr/bin/env bash
### DOC
# utils/backup - Backup important directories
#
# Usage: dr utils/backup [--compress]
#
# Options:
#   --compress    Compress backup with gzip
#   --exclude     Exclude pattern (can be used multiple times)
#
# Environment:
#   BACKUP_DIRS    Directories to backup (default: Documents, Pictures)
#   BACKUP_DEST    Backup destination (default: ~/Backups)
### DOC

set -euo pipefail

COMPRESS=false
EXCLUDES=()
BACKUP_DIRS="${BACKUP_DIRS:-Documents Pictures}"
BACKUP_DEST="${BACKUP_DEST:-$HOME/Backups}"

# Parse options
while [[ $# -gt 0 ]]; do
  case $1 in
    --compress)
      COMPRESS=true
      shift
      ;;
    --exclude)
      EXCLUDES+=("--exclude=$2")
      shift 2
      ;;
    *)
      echo "Unknown option: $1"
      exit 1
      ;;
  esac
done

# Create backup directory
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_PATH="$BACKUP_DEST/backup_$TIMESTAMP"
mkdir -p "$BACKUP_PATH"

echo "Starting backup to $BACKUP_PATH"

# Backup each directory
for dir in $BACKUP_DIRS; do
  if [[ -d "$HOME/$dir" ]]; then
    echo "Backing up $dir..."
    rsync -av "${EXCLUDES[@]}" "$HOME/$dir/" "$BACKUP_PATH/$dir/"
  else
    echo "Warning: $dir not found, skipping"
  fi
done

# Compress if requested
if [[ "$COMPRESS" == true ]]; then
  echo "Compressing backup..."
  tar -czf "$BACKUP_PATH.tar.gz" -C "$BACKUP_DEST" "backup_$TIMESTAMP"
  rm -rf "$BACKUP_PATH"
  echo "✓ Backup completed: $BACKUP_PATH.tar.gz"
else
  echo "✓ Backup completed: $BACKUP_PATH"
fi

# Show backup size
du -sh "$BACKUP_DEST/backup_$TIMESTAMP"*

System Info

#!/usr/bin/env bash
### DOC
# utils/sysinfo - Display system information
#
# Usage: dr utils/sysinfo [--json]
#
# Options:
#   --json    Output in JSON format
### DOC

set -euo pipefail

JSON_OUTPUT=false
[[ "${1:-}" == "--json" ]] && JSON_OUTPUT=true

# Gather info
HOSTNAME=$(hostname)
OS=$(uname -s)
KERNEL=$(uname -r)
ARCH=$(uname -m)
UPTIME=$(uptime -p 2>/dev/null || uptime)
CPU=$(grep -m1 "model name" /proc/cpuinfo 2>/dev/null | cut -d: -f2 | xargs)
MEMORY=$(free -h 2>/dev/null | awk '/^Mem:/ {print $2}')
DISK=$(df -h / | awk 'NR==2 {print $4 " free of " $2}')

if [[ "$JSON_OUTPUT" == true ]]; then
  cat <<EOF
{
  "hostname": "$HOSTNAME",
  "os": "$OS",
  "kernel": "$KERNEL",
  "architecture": "$ARCH",
  "uptime": "$UPTIME",
  "cpu": "$CPU",
  "memory": "$MEMORY",
  "disk": "$DISK"
}
EOF
else
  cat <<EOF
System Information
==================
Hostname:     $HOSTNAME
OS:           $OS
Kernel:       $KERNEL
Architecture: $ARCH
Uptime:       $UPTIME
CPU:          $CPU
Memory:       $MEMORY
Disk (root):  $DISK
EOF
fi

Team Examples

Shared Library

#!/usr/bin/env bash
# ~/.config/dotrun/bin/lib/common.sh
### DOC
# lib/common - Shared functions for team scripts
#
# Source this file in other scripts:
#   source "$(dirname "$0")/../lib/common.sh"
### DOC

# Color output functions
red() { echo -e "\033[31m$*\033[0m"; }
green() { echo -e "\033[32m$*\033[0m"; }
yellow() { echo -e "\033[33m$*\033[0m"; }
blue() { echo -e "\033[34m$*\033[0m"; }

# Logging functions
log() { echo "[$(date +'%Y-%m-%d %H:%M:%S')] $*"; }
error() { red "ERROR: $*" >&2; }
success() { green "SUCCESS: $*"; }
warning() { yellow "WARNING: $*"; }
info() { blue "INFO: $*"; }

# Confirmation prompt
confirm() {
  local prompt="${1:-Continue?}"
  local default="${2:-n}"

  if [[ "$default" == "y" ]]; then
    prompt="$prompt [Y/n] "
  else
    prompt="$prompt [y/N] "
  fi

  read -r -p "$prompt" response
  response=${response:-$default}

  [[ "$response" =~ ^[Yy]$ ]]
}

# Check dependencies
require_command() {
  local cmd="$1"
  if ! command -v "$cmd" &>/dev/null; then
    error "$cmd is required but not installed"
    return 1
  fi
}

# Retry function
retry() {
  local retries="${1:-3}"
  local delay="${2:-1}"
  shift 2

  local count=0
  until "$@"; do
    count=$((count + 1))
    if [[ $count -lt $retries ]]; then
      warning "Attempt $count failed, retrying in ${delay}s..."
      sleep "$delay"
    else
      error "Failed after $retries attempts"
      return 1
    fi
  done
}

Team Onboarding Script

#!/usr/bin/env bash
### DOC
# team/onboard - Set up new team member environment
#
# Usage: dr team/onboard <email>
#
# Sets up:
#   - Git configuration
#   - SSH keys
#   - Tool installation
#   - Project cloning
### DOC

set -euo pipefail

if [[ $# -eq 0 ]]; then
  echo "Error: Email address required"
  exit 1
fi

EMAIL="$1"
NAME=$(echo "$EMAIL" | cut -d@ -f1 | sed 's/[._]/ /g' | sed 's/\b\(.\)/\u\1/g')

echo "Welcome to the team, $NAME!"
echo "This script will set up your development environment."
echo ""

# Git configuration
echo "1. Configuring Git..."
git config --global user.email "$EMAIL"
git config --global user.name "$NAME"
git config --global init.defaultBranch main
git config --global pull.rebase true

# SSH key
echo "2. Setting up SSH key..."
if [[ ! -f ~/.ssh/id_ed25519 ]]; then
  ssh-keygen -t ed25519 -C "$EMAIL" -f ~/.ssh/id_ed25519 -N ""
  echo "✓ SSH key created"
  echo ""
  echo "Add this key to your GitHub account:"
  cat ~/.ssh/id_ed25519.pub
  echo ""
  read -p "Press Enter when you've added the key..."
else
  echo "✓ SSH key already exists"
fi

# Install tools
echo "3. Installing required tools..."
TOOLS="jq yq shellcheck"
for tool in $TOOLS; do
  if ! command -v "$tool" &>/dev/null; then
    echo "Installing $tool..."
    # Add platform-specific installation
  else
    echo "$tool already installed"
  fi
done

# Clone projects
echo "4. Cloning team projects..."
PROJECTS_DIR="$HOME/projects"
mkdir -p "$PROJECTS_DIR"

REPOS=(
  "git@github.com:team/main-app.git"
  "git@github.com:team/api.git"
  "git@github.com:team/docs.git"
)

for repo in "${REPOS[@]}"; do
  repo_name=$(basename "$repo" .git)
  if [[ ! -d "$PROJECTS_DIR/$repo_name" ]]; then
    echo "Cloning $repo_name..."
    git clone "$repo" "$PROJECTS_DIR/$repo_name"
  else
    echo "$repo_name already cloned"
  fi
done

# Import team scripts
echo "5. Importing team DotRun scripts..."
if [[ -f ~/Downloads/team-scripts.tar.gz ]]; then
  dr collection import ~/Downloads/team-scripts.tar.gz
else
  echo "Download team-scripts.tar.gz and run:"
  echo "  dr collection import ~/Downloads/team-scripts.tar.gz"
fi

echo ""
echo "✓ Onboarding complete!"
echo ""
echo "Next steps:"
echo "1. Review team documentation in $PROJECTS_DIR/docs"
echo "2. Run 'dr team/setup-dev' to configure development environment"
echo "3. Join #dev-team Slack channel"

Advanced Patterns

Parallel Processing

#!/usr/bin/env bash
### DOC
# advanced/parallel-process - Process files in parallel
#
# Usage: dr advanced/parallel-process <directory>
#
# Processes all .log files in parallel using GNU parallel
### DOC

set -euo pipefail

if [[ $# -eq 0 ]]; then
  echo "Error: Directory required"
  exit 1
fi

DIR="$1"

if ! command -v parallel &>/dev/null; then
  echo "Error: GNU parallel required"
  echo "Install: apt/brew install parallel"
  exit 1
fi

process_file() {
  local file="$1"
  echo "Processing: $file"

  # Simulate processing
  grep -c ERROR "$file" || echo "0 errors"

  # Real processing would go here
  # compress, analyze, upload, etc.
}

export -f process_file

# Find and process files in parallel
find "$DIR" -name "*.log" -type f \
  | parallel -j 4 --progress process_file

echo "✓ Processing complete"

State Machine

#!/usr/bin/env bash
### DOC
# advanced/state-machine - Example state machine implementation
#
# Usage: dr advanced/state-machine
#
# Demonstrates a simple state machine pattern
### DOC

set -euo pipefail

# States
STATE_INIT="init"
STATE_RUNNING="running"
STATE_PAUSED="paused"
STATE_STOPPED="stopped"

# Current state
CURRENT_STATE="$STATE_INIT"

# State transitions
transition() {
  local from="$1"
  local to="$2"

  echo "Transitioning from $from to $to"
  CURRENT_STATE="$to"

  # Execute state entry actions
  case "$to" in
    "$STATE_RUNNING")
      echo "Starting process..."
      ;;
    "$STATE_PAUSED")
      echo "Pausing process..."
      ;;
    "$STATE_STOPPED")
      echo "Stopping process..."
      ;;
  esac
}

# State machine loop
while true; do
  echo "Current state: $CURRENT_STATE"
  echo "Commands: start, pause, resume, stop, quit"
  read -p "> " command

  case "$CURRENT_STATE" in
    "$STATE_INIT")
      case "$command" in
        start)
          transition "$STATE_INIT" "$STATE_RUNNING"
          ;;
        quit)
          break
          ;;
        *)
          echo "Invalid command in INIT state"
          ;;
      esac
      ;;

    "$STATE_RUNNING")
      case "$command" in
        pause)
          transition "$STATE_RUNNING" "$STATE_PAUSED"
          ;;
        stop)
          transition "$STATE_RUNNING" "$STATE_STOPPED"
          ;;
        *)
          echo "Invalid command in RUNNING state"
          ;;
      esac
      ;;

    "$STATE_PAUSED")
      case "$command" in
        resume)
          transition "$STATE_PAUSED" "$STATE_RUNNING"
          ;;
        stop)
          transition "$STATE_PAUSED" "$STATE_STOPPED"
          ;;
        *)
          echo "Invalid command in PAUSED state"
          ;;
      esac
      ;;

    "$STATE_STOPPED")
      case "$command" in
        start)
          transition "$STATE_STOPPED" "$STATE_RUNNING"
          ;;
        quit)
          break
          ;;
        *)
          echo "Invalid command in STOPPED state"
          ;;
      esac
      ;;
  esac
done

echo "State machine terminated"

Want more examples? Check out:

← Back to README | Home | Troubleshooting → | Top ↑

Clone this wiki locally