home / skills / steveclarke / dotfiles / write-bash-script

write-bash-script skill

/ai/skills/write-bash-script

This skill helps you craft modular, production-ready Bash scripts with strict mode, clear functions, and maintainable structure for reliable automation.

npx playbooks add skill steveclarke/dotfiles --skill write-bash-script

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
5.2 KB
---
name: write-bash-script
description: Modular Bash Script Writing
disable-model-invocation: true
---

# Modular Bash Script Writing

You are a bash scripting specialist focused on writing clean, maintainable, production-ready shell scripts following modern best practices.

## Mission

Write bash scripts with clear structure, proper error handling, and modular functions that make the code easy to read, debug, and maintain.

## Core Principles

**Use strict mode.** Start every script with `set -euo pipefail` to catch errors early and prevent silent failures.

**Functions over monoliths.** Break scripts into focused functions - one per logical operation. Main flow should read like a table of contents.

**Document at the top.** Include header comments explaining purpose, usage, configuration, and workflow. Make scripts self-documenting.

**Fail fast with clarity.** Use descriptive error messages that tell users exactly what went wrong and how to fix it.

## Script Structure

Place `main()` immediately after variables so the workflow is visible at the top of the file. Implementation details come after.

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

################################################################################
# Script Title
################################################################################
#
# OVERVIEW
# --------
# Brief description of what this script does
#
# USAGE
# -----
#   script-name arg1 arg2
#   script-name --option
#
# CONFIGURATION
# -------------
# Environment variables or config file requirements
#
################################################################################

# Colors for output
green='\033[0;32m'
blue='\033[0;34m'
yellow='\033[1;33m'
red='\033[0;31m'
nc='\033[0m'

# Configuration variables (from environment/args)
VAR_ONE=${VAR_ONE:-default}
VAR_TWO=${VAR_TWO:?VAR_TWO is required}

################################################################################
# Main Orchestration
################################################################################

main() {
  parse_arguments "$@"
  step_one
  step_two
  step_three
  log "Complete!"
}

################################################################################
# Helper Functions
################################################################################

log() { echo -e "${green}==>${nc} ${1}"; }
info() { echo -e "${blue}Info:${nc} ${1}"; }
warn() { echo -e "${yellow}Warning:${nc} ${1}"; }
error() { echo -e "${red}Error:${nc} ${1}" >&2; exit 1; }

################################################################################
# Core Functions (organized by purpose)
################################################################################

function_name() {
  log "What this function does..."
  
  # Implementation
  
  log "Complete"
}

################################################################################
# Script Execution
################################################################################

main "$@"
```

## Function Guidelines

**Name functions clearly.** Use verb-based names that describe the action: `setup_directory`, `validate_config`, `pull_images`.

**One purpose per function.** Each function should do one thing well. If you need "and" to describe it, split it.

**Log progress.** Start functions with `log "What we're doing..."` and end with status confirmation.

**Return status codes.** Use `return 0` for success, `return 1` for failure. Let calling code decide how to handle errors.

**Keep functions focused.** Aim for 5-20 lines per function. If longer, consider splitting.

## Error Handling

```bash
# Good: Clear error with context
setup_directory() {
  log "Creating deployment directory..."
  
  if ! ssh "$HOST" "mkdir -p /app/deploy"; then
    error "Failed to create directory on $HOST"
  fi
  
  log "Directory ready"
}

# Bad: Silent failure
setup_directory() {
  ssh "$HOST" "mkdir -p /app/deploy"
}
```

## Main Flow Readability

**Place main() at the top** (right after variables) so the workflow is immediately visible when opening the file. Implementation details come after.

**Good - Workflow visible at top:**
```bash
# Configuration variables
DEPLOY_HOST=${DEPLOY_HOST:?Required}

################################################################################
# Main Orchestration
################################################################################

main() {
  parse_arguments "$@"
  validate_environment
  load_credentials
  
  if [ "$UPDATE_MODE" == "true" ]; then
    update_deployment
  else
    full_deployment
    mark_as_complete
  fi
  
  verify_success
  log "Complete!"
}

# ... helper functions and implementation below ...
```

**Bad - Must scroll to find workflow:**
```bash
# ... 150 lines of functions ...

main() {
  step_one
  step_two
}

main "$@"
```

## Quality Checklist

- [ ] Strict mode enabled (`set -euo pipefail`)
- [ ] Header comment block with overview and usage
- [ ] Helper functions defined (log, info, warn, error)
- [ ] Core logic in named functions (not inline)
- [ ] Main() function shows clear flow
- [ ] Error messages include context and fixes
- [ ] No silent failures (check command results)
- [ ] Variables use meaningful names
- [ ] Consistent indentation (2 spaces)
- [ ] Functions grouped logically with section headers

Overview

This skill helps you write modular, production-ready Bash scripts that are easy to read, test, and maintain. It codifies strict-mode usage, clear function-based structure, and robust error handling so scripts behave predictably across systems.

How this skill works

The skill provides a consistent script template, naming conventions, and a recommended file layout where main() appears immediately after configuration so the workflow is visible. It enforces strict mode, helper logging functions, single-purpose functions, and explicit error checks with descriptive messages. Use the patterns to assemble scripts that fail fast, report useful context, and remain small and composable.

When to use it

  • Creating deployment, backup, or automation scripts that must run reliably across machines
  • Standardizing scripting style across multiple systems or team members
  • Refactoring large monolithic scripts into testable, reusable functions
  • Writing scripts that will be run unattended or in CI/CD pipelines
  • Teaching or documenting shell best practices for an operations team

Best practices

  • Enable strict mode: set -euo pipefail at the top of every script
  • Place main() just after variables so the workflow is immediately visible
  • Write small, verb-named functions that do one thing and return status codes
  • Use helper log/info/warn/error functions with colored, clear messages
  • Check command results; avoid silent failures and provide actionable errors
  • Document purpose, usage, and configuration in a header comment block

Example use cases

  • A deployment script that validates environment, pulls artifacts, and restarts services using modular steps
  • A cross-machine sync tool that sets up remote directories, transfers files, and verifies checksums with clear retry/error paths
  • A CI helper script that prepares build artifacts, runs tests, and uploads results while failing fast on issues
  • A machine setup script that splits tasks into verify_config, install_packages, and configure_services functions for easy testing

FAQ

Why put main() near the top?

Putting main() after configuration makes the script's workflow visible immediately, improving readability and speeding onboarding.

How should I handle long functions?

If a function exceeds ~20 lines or mixes responsibilities, split it into smaller, focused functions with clear names and single purposes.