Scripted OpsAutomate the boring stuff.

Bash Public-Release Script Template (Safe Defaults, Logging, Dry-Run, Strict Mode)

bash

A reusable Bash template designed for scripts you plan to publish publicly. Includes strict mode, dependency checks, parameter parsing, safe logging, dry-run support, and environment-agnostic placeholders suitable for homelabs and small production systems.

bashshelltemplateopen-sourceportfoliobest practicesloggingsafetyautomation

Script Code

Bashbash
#!/usr/bin/env bash
# -----------------------------------------------------------------------------
# ScriptedOps Public-Release Bash Template
#
# Purpose:
#   A safe, reusable Bash template for scripts you intend to publish publicly.
#
# Features:
#   - Strict mode (safer scripting)
#   - Dependency checks
#   - Argument parsing + usage
#   - Dry-run mode (no changes)
#   - Logging to console and optional file
#   - Environment-agnostic placeholders (no internal names)
#
# Security / Privacy:
#   - Do not hardcode credentials, tokens, internal hostnames, or domains
#   - Do not include real data samples in comments or output examples
#   - Use placeholders and document required inputs clearly
#
# License: MIT (recommended)
# -----------------------------------------------------------------------------

set -euo pipefail
IFS=$'\n\t'

# -----------------------------
# Defaults (safe + generic)
# -----------------------------
TARGET=""
LOG_FILE=""
DRY_RUN="false"
VERBOSE="false"

# -----------------------------
# Helpers
# -----------------------------
timestamp() { date +"%Y-%m-%d %H:%M:%S"; }

log() {
  # Usage: log LEVEL MESSAGE...
  local level="$1"; shift
  local msg="$*"
  local line
  line="$(timestamp) [$level] $msg"

  # Console
  case "$level" in
    ERROR) echo "$line" >&2 ;;
    WARN)  echo "$line" ;;
    DEBUG) if [[ "$VERBOSE" == "true" ]]; then echo "$line"; fi ;;
    INFO|*) echo "$line" ;;
  esac

  # File
  if [[ -n "$LOG_FILE" ]]; then
    mkdir -p "$(dirname "$LOG_FILE")" 2>/dev/null || true
    echo "$line" >> "$LOG_FILE"
  fi
}

die() {
  log ERROR "$*"
  exit 1
}

usage() {
  cat <<'EOF'
Usage:
  script.sh --target PATH [--log FILE] [--dry-run] [--verbose]

Options:
  --target PATH   Required. The main target path/resource for the script.
  --log FILE      Optional. Log file path. If omitted, logs to console only.
  --dry-run       Optional. Preview actions without making changes.
  --verbose       Optional. Extra debug logging.
  -h, --help      Show this help text.

Examples:
  # Preview actions
  ./script.sh --target /var/log --dry-run

  # Run with logging
  ./script.sh --target /var/log --log /var/log/scriptedops-template.log

Notes:
  - Keep this script environment-agnostic for public release.
  - Do not hardcode secrets or internal hostnames.
EOF
}

require_cmd() {
  command -v "$1" >/dev/null 2>&1 || die "Missing required command: $1"
}

assert_target_exists() {
  [[ -e "$1" ]] || die "Target does not exist: $1"
}

# -----------------------------
# Argument parsing
# -----------------------------
parse_args() {
  while [[ $# -gt 0 ]]; do
    case "$1" in
      --target)
        TARGET="${2:-}"; shift 2 ;;
      --log)
        LOG_FILE="${2:-}"; shift 2 ;;
      --dry-run)
        DRY_RUN="true"; shift ;;
      --verbose)
        VERBOSE="true"; shift ;;
      -h|--help)
        usage; exit 0 ;;
      *)
        die "Unknown argument: $1 (use --help)" ;;
    esac
  done

  [[ -n "$TARGET" ]] || die "--target is required (use --help)"
}

# -----------------------------
# Main logic (replace placeholders)
# -----------------------------
main() {
  parse_args "$@"

  # Dependencies (adjust per script)
  require_cmd find
  require_cmd awk

  log INFO "=== Start ==="
  log INFO "Target=$TARGET DryRun=$DRY_RUN LogFile=${LOG_FILE:-<none>} Verbose=$VERBOSE"

  assert_target_exists "$TARGET"

  # Example "work items" (replace with your script’s real target discovery)
  # Here: list files under TARGET (depth 1) as a demo.
  mapfile -t items < <(find "$TARGET" -maxdepth 1 -type f 2>/dev/null || true)

  log INFO "Discovered ${#items[@]} item(s) to process."

  for item in "${items[@]}"; do
    # Describe intended action
    log DEBUG "Candidate: $item"

    if [[ "$DRY_RUN" == "true" ]]; then
      log INFO "DRY RUN: Would process $item"
      continue
    fi

    # === PLACEHOLDER ACTION ===
    # Replace with real operation, e.g.:
    #   rm -f "$item"
    #   chmod 600 "$item"
    #   curl -X POST ...
    #   systemctl restart ...
    #
    # Wrap risky actions with error handling:
    if ! true; then
      log WARN "Non-fatal failure processing: $item"
      continue
    fi

    log INFO "Processed: $item"
  done

  log INFO "=== End ==="
}

# -----------------------------
# Entrypoint
# -----------------------------
main "$@"

Get new scripts weekly.

Subscribe to receive fresh PowerShell scripts, automation tips, and IT workflow improvements.

Subscribe to Newsletter