- Posted on
- • Artificial Intelligence
Create Self-Documenting Bash Scripts with Artificial Intelligence
- Author
-
-
- User
- linuxbash
- Posts by this author
- Posts by this author
-
Create Self-Documenting Bash Scripts with Artificial Intelligence
Ever joined a project where the Bash scripts were powerful but opaque—no --help, a stale README, and comments that don’t match the code anymore? What if scripts could explain themselves and keep their own documentation fresh automatically?
This post shows you how to build self-documenting Bash scripts and use AI to keep their help text, examples, and man pages in sync with the code—so your docs never drift again.
Why this matters
Documentation drift is real: CLI flags evolve faster than READMEs.
Bash scripts live a long time and get copied everywhere—help output is the one piece of documentation that always travels with the script.
AI can summarize code, infer usage and options, and write crisp examples… but only if you give it a predictable place to put the output.
With a little structure (and common Linux tools), you can keep docs up-to-date on every commit, even offline with local models.
What you’ll build
A simple, parseable header block inside your Bash scripts (source of truth)
An internal
--helpthat prints this headerAutomatic man page generation from
--helpAn AI-powered generator that updates the header from your code
Optional git hook to keep everything in sync
Prerequisites and installation
Install the common tools we’ll use:
git, curl, jq
shellcheck (lint)
shfmt (format)
help2man (man page generation)
pipx (to install Python CLIs in isolated environments)
Debian/Ubuntu (apt):
sudo apt update
sudo apt install -y git curl jq shellcheck shfmt help2man pipx
# If pipx isn’t packaged on your distro:
# sudo apt install -y python3-pip
# python3 -m pip install --user pipx
# python3 -m pipx ensurepath
Fedora/RHEL (dnf):
sudo dnf install -y git curl jq ShellCheck shfmt help2man pipx
openSUSE (zypper):
sudo zypper install -y git curl jq ShellCheck shfmt help2man pipx
AI options (pick one):
Local (no API key):
curl -fsSL https://ollama.com/install.sh | sh # Fetch a small, general model ollama pull llama3.1Cloud via a general-purpose CLI:
pipx install llm # Then configure a provider/key per the llm docs (e.g., OpenAI, Anthropic, etc.) # Example for OpenAI (after setting OPENAI_API_KEY): # llm keys set openai
Tip: After installing pipx, you may need to open a new shell or run pipx ensurepath.
1) Add a parseable documentation header
Put a tightly structured header in your script that your code can print verbatim. Prefix doc lines with #% so they’re easy to extract.
Example: backup-s3 (skeleton)
#!/usr/bin/env bash
set -euo pipefail
#% BEGIN AI DOC
#% Name: backup-s3
#% Description: Sync a local directory to an S3 bucket with retries and logging.
#% Usage: backup-s3 [-n] [-r N] [-v] SRC_DIR S3_URI
#% Options:
#% -n, --dry-run Show what would be done without actually syncing
#% -r, --retries N Number of retries on failure (default: 3)
#% -v, --verbose Increase logging verbosity (repeatable)
#% -h, --help Show help and exit
#% Examples:
#% backup-s3 -r 5 ./site s3://my-bucket/site
#% backup-s3 --dry-run ./data s3://my-bucket/data
#% END AI DOC
usage() {
# Print lines that start with "#% " (strip the marker)
sed -n 's/^#% \{0,1\}//p' "$0"
}
dry_run=0 retries=3 verbose=0
while [[ $# -gt 0 ]]; do
case "$1" in
-n|--dry-run) dry_run=1; shift ;;
-r|--retries) retries="${2:-}"; shift 2 ;;
-v|--verbose) verbose=$((verbose+1)); shift ;;
-h|--help) usage; exit 0 ;;
--) shift; break ;;
-*) echo "Unknown option: $1" >&2; usage; exit 2 ;;
*) break ;;
esac
done
# Guard required args
if [[ $# -lt 2 ]]; then
echo "Error: missing SRC_DIR and S3_URI" >&2
echo >&2
usage >&2
exit 2
fi
src="$1"; dest="$2"
# Your implementation here (e.g., using aws s3 sync).
# For demo purposes, we only echo the parsed intent.
if (( dry_run )); then echo "[DRY RUN]"; fi
echo "Would sync '$src' -> '$dest' with retries=$retries verbose=$verbose"
Now your script can always explain itself:
./backup-s3 --help
This header block is the one place that must remain correct—so let’s make AI maintain it for you.
2) Turn your header into --help and a man page
You already have usage() to print help. Next, generate a man page with help2man, which reads --help output:
help2man -N -n "Sync a local directory to S3 with retries" \
-o man/backup-s3.1 ./backup-s3
-Ndisables “report bugs” footer-nsets the short description
Distribute man/backup-s3.1 along with your script, or add a Makefile target to regenerate it on demand.
3) Use AI to update the header from the code
Create a tiny tool that asks an AI model to summarize your script into the #% header format and either print it or update in place.
Save as tools/gen-help:
#!/usr/bin/env bash
set -euo pipefail
script="${1:-}"
[[ -z "$script" || ! -f "$script" ]] && {
echo "Usage: $0 <script.sh> [-i]" >&2
exit 2
}
inplace=0
[[ "${2:-}" == "-i" ]] && inplace=1
read -r -d '' PROMPT <<'EOF'
You are generating a documentation header for a Bash script.
Output ONLY lines starting with "#% " using this template:
#% Name: <script-name>
#% Description: <one concise line>
#% Usage: <command and flags/args>
#% Options:
#% -x, --long <description>
#% Examples:
#% <example 1>
#% <example 2>
Keep it accurate, minimal, and consistent with the code.
EOF
generate() {
# Prefer local (no key) if available
if command -v ollama >/dev/null 2>&1; then
{ printf '%s\n\n' "$PROMPT"; cat "$script"; } | ollama run llama3.1
return
fi
# Fallback to "llm" CLI (configured with your provider/key)
if command -v llm >/dev/null 2>&1; then
# llm reads prompt from stdin; -s sets a system message
# We pass PROMPT as system and the code on stdin
LLM_MODEL="${LLM_MODEL:-gpt-4o-mini}"
cat "$script" | llm -m "$LLM_MODEL" -s "$PROMPT"
return
fi
echo "No AI CLI found. Install one of: ollama, llm" >&2
exit 1
}
out="$(generate | sed -e '/^```/d')"
if (( inplace )); then
awk -v hdr="$out" '
BEGIN{placed=0}
/^#% BEGIN AI DOC/ {print; print hdr; skip=1; next}
/^#% END AI DOC/ {skip=0; placed=1; next}
!skip {print}
END{
if(!placed){
print "#% BEGIN AI DOC"
print hdr
print "#% END AI DOC"
}
}
' "$script" > "$script.tmp"
mv "$script.tmp" "$script"
echo "Updated header in $script"
else
printf '%s\n' "$out"
fi
Make it executable:
chmod +x tools/gen-help
Usage:
Preview docs:
tools/gen-help ./backup-s3Update in place:
tools/gen-help ./backup-s3 -i
Notes:
For local inference,
ollama pull llama3.1first.For cloud, install
pipx install llm, set a provider and key, and optionally exportLLM_MODEL=gpt-4o-mini(or a model you have access to).
4) Add guardrails: format, lint, and refresh docs pre-commit
Keep your scripts healthy and your docs current by wiring this into git.
Create .git/hooks/pre-commit:
#!/usr/bin/env bash
set -euo pipefail
changed=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.sh$' || true)
[[ -z "$changed" ]] && exit 0
echo "[pre-commit] Formatting with shfmt..."
shfmt -w ${changed}
echo "[pre-commit] Linting with shellcheck..."
for f in ${changed}; do shellcheck "$f"; done
echo "[pre-commit] Refreshing AI docs..."
for f in ${changed}; do ./tools/gen-help "$f" -i; done
git add ${changed}
Then:
chmod +x .git/hooks/pre-commit
Every commit will now:
Reformat with
shfmtLint with
shellcheckRebuild the
#%header using your chosen AI tool
This keeps your help text aligned with your actual flags and behavior.
5) Optional: Regenerate man pages and README
Add simple Make targets you can run locally or in CI:
# Makefile
SCRIPTS := backup-s3
MAN_DIR := man
.PHONY: help docs man
help:
@echo "make docs - refresh AI headers"
@echo "make man - build man pages"
docs:
@for s in $(SCRIPTS); do ./tools/gen-help $$s -i; done
man:
@mkdir -p $(MAN_DIR)
@for s in $(SCRIPTS); do \
help2man -N -n "$$s manual" -o $(MAN_DIR)/$$s.1 ./$$s ; \
done
Run:
make docs
make man
You can also generate a README section from --help:
./backup-s3 --help | sed '1s/^/# /; 2,$$s/^/ /' > README.md
Real-world tips
Keep the header minimal. Your
--helpshould fit one terminal screen.Prefer short flags with long equivalents (
-r, --retries) and show defaults explicitly.Ask AI for 1–2 realistic examples; delete anything you can’t test or guarantee.
Trust but verify: AI can be confidently wrong. Run the script with sample inputs and confirm help accuracy.
For private codebases, prefer local models via Ollama or on-prem providers.
Conclusion and next steps
Self-documenting Bash scripts reduce onboarding friction, prevent drift, and make your tooling discoverable. Add a parseable header, print it with --help, wire up help2man, and let AI keep that header up to date as you refactor.
Your next steps:
1) Add the #% header and usage() to one script you use daily.
2) Install the tooling:
- apt: sudo apt install -y git curl jq shellcheck shfmt help2man pipx
- dnf: sudo dnf install -y git curl jq ShellCheck shfmt help2man pipx
- zypper: sudo zypper install -y git curl jq ShellCheck shfmt help2man pipx
3) Choose an AI path:
- Local: curl -fsSL https://ollama.com/install.sh | sh && ollama pull llama3.1
- Cloud: pipx install llm and set a provider/key
4) Drop in tools/gen-help, run ./tools/gen-help your-script.sh -i
5) Add the pre-commit hook to keep everything fresh
Make your scripts your own documentation—so teammates (and future you) can just run --help and get to work.