Structured Bash Script Generator
What You'll Do
- 📥 Gather the script's goal, required positional/flag arguments, environment variables, and external program dependencies
- 🧱 Produce a Bash 3.2-compatible script skeleton with a
check_requirements function that validates inputs and dependencies kindly
- 🛡️ Ensure the script sets safe defaults (
set -euo pipefail), quotes expansions, and keeps logic portable to macOS/Linux Bash 3.2
- ✨ Format the script with
shfmt when available and return a polished result ready for immediate use
When to Use This Skill
Use this skill whenever the user asks for a new bash script or a major refactor of an existing script and they expect:
- Guardrails around required arguments, environment variables, or external tools
- Friendly, actionable error messages when prerequisites are missing
- Compatibility with older Bash versions (macOS default 3.2)
Do not use this skill for:
- POSIX
sh-only scripts (no Bash-specific features allowed)
- Small one-liners or trivial command snippets (respond inline instead)
- Advanced Bash (>3.2) needs such as associative arrays or
coproc
Phase 1 · Clarify the Script Brief
- Confirm the script's purpose, expected inputs, outputs, and typical usage examples.
- Identify all positional arguments and flags that must be provided. Capture human-friendly labels for each so the usage text and errors are clear.
- List required environment variables (names + meaning) and external commands (e.g.,
curl, jq). Note install hints when useful.
- Ask about optional inputs or defaults that should be applied when values are omitted.
- Determine whether the script writes files, consumes stdin/stdout, or needs cleanup logic.
Deliverable: A short table (in notes or your head) of arguments, env vars, and commands you will feed into check_requirements and usage messaging.
Phase 2 · Plan the Script Structure
Lay out the sections before writing code:
Header & Safety
#!/usr/bin/env bash
set -euo pipefail
IFS=$'\n\t' only if tighter word splitting is needed.
Metadata Comments (optional)
- Summarize script purpose and prerequisites in commented lines for discoverability.
Usage Helper
- A
usage() function that prints how to run the script, expected args, environment variables, and examples.
Requirement Configuration
- Define
REQUIRED_ARGS, REQUIRED_ENV_VARS, and REQUIRED_PROGRAMS as indexed arrays (compatible with Bash 3.2). When nothing is required, keep the arrays empty but present.
- Optionally define associative-looking notes via comments or simple
case statements; do not use declare -A (requires Bash ≥4).
check_requirements Function (see Phase 3 for exact pattern)
- Accepts parsed arguments (or a struct) and validates all prerequisites.
- Emits kind, actionable errors to STDERR and returns non-zero on failure.
Argument Parsing
- Prefer
getopts for short flags. For long options, parse manually with a while loop; avoid getopt if portability is uncertain.
- Populate variables for downstream logic (use
${VAR:-} to coexist with set -u).
Main Logic
- Encapsulate primary workflow in
main() and finish with main "$@".
Phase 3 · Compose the Script
Follow this recipe while writing the actual script content.
Required Guardrail: check_requirements
check_requirements() {
local -r provided_arg_count=$1
local missing=0
if [ ${#REQUIRED_ARGS[@]} -gt 0 ] && [ "$provided_arg_count" -lt ${#REQUIRED_ARGS[@]} ]; then
printf 'Error: Expected %s arguments (%s) but received %s.\n' \
${#REQUIRED_ARGS[@]} "${REQUIRED_ARGS[*]}" "$provided_arg_count" >&2
missing=1
fi
local env_var
for env_var in "${REQUIRED_ENV_VARS[@]}"; do
if [ -z "${!env_var:-}" ]; then
printf 'Error: Missing required environment variable %s. Please set it before rerunning.\n' "$env_var" >&2
missing=1
fi
done
local program
for program in "${REQUIRED_PROGRAMS[@]}"; do
if ! command -v "$program" >/dev/null 2>&1; then
printf 'Error: Required program %s is not installed or not on PATH. Please install it first.\n' "$program" >&2
missing=1
fi
done
if [ "$missing" -ne 0 ]; then
printf '\n' >&2
usage >&2
return 1
fi
}
Implementation notes:
- Always invoke
check_requirements right after argument parsing, e.g. check_requirements "$#".
- If the script allows optional trailing arguments, keep
REQUIRED_ARGS limited to the mandatory ones and validate optional parameters separately after check_requirements "$#" succeeds.
- Keep error language supportive (“Please install…”) rather than punitive.
- Route any diagnostics to STDERR (
>&2) and exit gracefully with return 1 so the caller can exit 1 or handle it.
- Only call
usage from error paths (like failed requirement checks) so successful runs stay quiet unless the user explicitly asks for help.
Bash 3.2 Compatibility Guardrails
- Use indexed arrays only; no associative arrays or namerefs (
local -n).
- Avoid
[[ string =~ regex ]] with capture groups that rely on Bash ≥3.2. Basic regex is fine, but keep patterns simple.
- Do not rely on
mapfile, readarray, coproc, printf -v, or process substitution that requires /dev/fd (often missing on macOS).
- Prefer
$( command ) subshells over backticks and quote every expansion.
- Use
printf instead of echo -e for reliable escape handling.
Usage Function Pattern
usage() {
cat <<'EOF'
Usage: my_script.sh <source> <destination> [--dry-run]
Required arguments:
source Path to the input file (must exist)
destination Output directory (will be created if missing)
Environment variables:
API_TOKEN Token used to authenticate API requests
External tools:
curl, jq
Examples:
my_script.sh ./input.csv ./out --dry-run
EOF
}
Tailor the body to the specific script; keep instructions kind and explicit.
Script Assembly Checklist
- Write header, safety settings, and optional metadata comments.
- Define requirement arrays (even if empty) and defaults for optional values.
- Implement
usage() and check_requirements() exactly once.
- Parse arguments safely (
getopts or manual loop) and convert into named variables.
- Call
check_requirements immediately after parsing. If it fails, exit with exit 1.
- Implement
main() with clear, modular helpers; rely on functions instead of sprawling inline code.
- End with
main "$@" and ensure the script returns appropriate exit codes.
Phase 4 · Validate, Format, and Hand Off
Self-check
- Does the script run without arguments and show
usage?
- Do missing env vars and programs produce the friendly errors described earlier?
- Do all branches respect
set -euo pipefail (guard nullable variables with ${VAR:-})?
Formatting via shfmt
- Detect availability:
if command -v shfmt >/dev/null 2>&1; then ... fi
- Run
shfmt -i 2 -bn -ci -sr -w <path-to-script> after writing the file.
- Mention in your response whether formatting ran or was skipped (and why).
Final Response Checklist
- Provide the complete script in a fenced code block (label it
bash).
- Summarize how requirements are enforced.
- If manual formatting was necessary (no
shfmt), note it explicitly.
- Suggest any quick validation commands (dry runs, linting) if relevant.
Reference Template
Use this skeleton as a starting point and adapt each section based on the user's requirements:
#!/usr/bin/env bash
set -euo pipefail
# Script: <name>
# Purpose: <one-line description>
# Requirements: <short summary of args/env/programs>
REQUIRED_ARGS=("arg1" "arg2")
REQUIRED_ENV_VARS=("ENV_VAR")
REQUIRED_PROGRAMS=("curl" "jq")
usage() {
cat <<'EOF'
Usage: <script-name> <arg1> <arg2>
Required arguments:
arg1 <describe>
arg2 <describe>
Environment variables:
ENV_VAR <describe>
External tools:
curl, jq
EOF
}
check_requirements() {
local -r provided_arg_count=$1
local missing=0
if [ ${#REQUIRED_ARGS[@]} -gt 0 ] && [ "$provided_arg_count" -lt ${#REQUIRED_ARGS[@]} ]; then
printf 'Error: Expected %s arguments (%s) but received %s.\n' \
${#REQUIRED_ARGS[@]} "${REQUIRED_ARGS[*]}" "$provided_arg_count" >&2
missing=1
fi
local env_var
for env_var in "${REQUIRED_ENV_VARS[@]}"; do
if [ -z "${!env_var:-}" ]; then
printf 'Error: Missing required environment variable %s. Please set it before rerunning.\n' "$env_var" >&2
missing=1
fi
done
local program
for program in "${REQUIRED_PROGRAMS[@]}"; do
if ! command -v "$program" >/dev/null 2>&1; then
printf 'Error: Required program %s is not installed or not on PATH. Please install it first.\n' "$program" >&2
missing=1
fi
done
if [ "$missing" -ne 0 ]; then
printf '\n' >&2
usage >&2
return 1
fi
}
parse_args() {
# TODO: replace with real parsing
SOURCE=${1:-}
DEST=${2:-}
}
main() {
parse_args "$@"
check_requirements "$#" || exit 1
# TODO: script logic goes here
printf 'Running with source=%s dest=%s\n' "$SOURCE" "$DEST"
}
main "$@"
Update placeholders, replace TODO sections, and adjust arrays when a requirement does not apply (leave the array empty—do not delete it).
Quality Checklist Before Finishing
1---2name: bash-script-generator3description: Generate Bash 3.2-compatible scripts with a standardized check_requirements guardrail, friendly validation errors, and optional shfmt formatting. Use when writing or updating bash scripts that need consistent argument validation, dependency checks, and portability.4license: MIT5---6
7# Structured Bash Script Generator
8
9## What You'll Do
10- 📥 Gather the script's goal, required positional/flag arguments, environment variables, and external program dependencies
11- 🧱 Produce a Bash 3.2-compatible script skeleton with a `check_requirements` function that validates inputs and dependencies kindly
12- 🛡️ Ensure the script sets safe defaults (`set -euo pipefail`), quotes expansions, and keeps logic portable to macOS/Linux Bash 3.2
13- ✨ Format the script with `shfmt` when available and return a polished result ready for immediate use
14
15---
16
17## When to Use This Skill
18Use this skill whenever the user asks for a new bash script or a major refactor of an existing script and they expect:
19- Guardrails around required arguments, environment variables, or external tools
20- Friendly, actionable error messages when prerequisites are missing
21- Compatibility with older Bash versions (macOS default 3.2)
22
23Do **not** use this skill for:
24- POSIX `sh`-only scripts (no Bash-specific features allowed)
25- Small one-liners or trivial command snippets (respond inline instead)
26- Advanced Bash (>3.2) needs such as associative arrays or `coproc`
27
28---
29
30## Phase 1 · Clarify the Script Brief
311. Confirm the script's purpose, expected inputs, outputs, and typical usage examples.
322. Identify all positional arguments and flags that must be provided. Capture human-friendly labels for each so the usage text and errors are clear.
333. List required environment variables (names + meaning) and external commands (e.g., `curl`, `jq`). Note install hints when useful.
344. Ask about optional inputs or defaults that should be applied when values are omitted.
355. Determine whether the script writes files, consumes stdin/stdout, or needs cleanup logic.
36
37> **Deliverable:** A short table (in notes or your head) of arguments, env vars, and commands you will feed into `check_requirements` and `usage` messaging.
38
39---
40
41## Phase 2 · Plan the Script Structure
42Lay out the sections before writing code:
43
441. **Header & Safety**
45 - `#!/usr/bin/env bash`
46 - `set -euo pipefail`
47 - `IFS=$'\n\t'` only if tighter word splitting is needed.
48
492. **Metadata Comments (optional)**
50 - Summarize script purpose and prerequisites in commented lines for discoverability.
51
523. **Usage Helper**
53 - A `usage()` function that prints how to run the script, expected args, environment variables, and examples.
54
554. **Requirement Configuration**
56 - Define `REQUIRED_ARGS`, `REQUIRED_ENV_VARS`, and `REQUIRED_PROGRAMS` as indexed arrays (compatible with Bash 3.2). When nothing is required, keep the arrays empty but present.
57 - Optionally define associative-looking notes via comments or simple `case` statements; do **not** use `declare -A` (requires Bash ≥4).
58
595. **check_requirements Function** (see Phase 3 for exact pattern)
60 - Accepts parsed arguments (or a struct) and validates all prerequisites.
61 - Emits kind, actionable errors to STDERR and returns non-zero on failure.
62
636. **Argument Parsing**
64 - Prefer `getopts` for short flags. For long options, parse manually with a `while` loop; avoid `getopt` if portability is uncertain.
65 - Populate variables for downstream logic (use `${VAR:-}` to coexist with `set -u`).
66
677. **Main Logic**
68 - Encapsulate primary workflow in `main()` and finish with `main "$@"`.
69
70---
71
72## Phase 3 · Compose the Script
73Follow this recipe while writing the actual script content.
74
75### Required Guardrail: `check_requirements`
76```bash
77check_requirements() {
78 local -r provided_arg_count=$1
79 local missing=0
80
81 if [ ${#REQUIRED_ARGS[@]} -gt 0 ] && [ "$provided_arg_count" -lt ${#REQUIRED_ARGS[@]} ]; then
82 printf 'Error: Expected %s arguments (%s) but received %s.\n' \
83 ${#REQUIRED_ARGS[@]} "${REQUIRED_ARGS[*]}" "$provided_arg_count" >&2
84 missing=1
85 fi
86
87 local env_var
88 for env_var in "${REQUIRED_ENV_VARS[@]}"; do
89 if [ -z "${!env_var:-}" ]; then
90 printf 'Error: Missing required environment variable %s. Please set it before rerunning.\n' "$env_var" >&2
91 missing=1
92 fi
93 done
94
95 local program
96 for program in "${REQUIRED_PROGRAMS[@]}"; do
97 if ! command -v "$program" >/dev/null 2>&1; then
98 printf 'Error: Required program %s is not installed or not on PATH. Please install it first.\n' "$program" >&2
99 missing=1
100 fi
101 done
102
103 if [ "$missing" -ne 0 ]; then
104 printf '\n' >&2
105 usage >&2
106 return 1
107 fi
108}
109```
110
111**Implementation notes:**
112- Always invoke `check_requirements` right after argument parsing, e.g. `check_requirements "$#"`.
113- If the script allows optional trailing arguments, keep `REQUIRED_ARGS` limited to the mandatory ones and validate optional parameters separately after `check_requirements "$#"` succeeds.
114- Keep error language supportive (“Please install…”) rather than punitive.
115- Route any diagnostics to STDERR (`>&2`) and exit gracefully with `return 1` so the caller can `exit 1` or handle it.
116- Only call `usage` from error paths (like failed requirement checks) so successful runs stay quiet unless the user explicitly asks for help.
117
118### Bash 3.2 Compatibility Guardrails
119- Use indexed arrays only; no associative arrays or namerefs (`local -n`).
120- Avoid `[[ string =~ regex ]]` with capture groups that rely on Bash ≥3.2. Basic regex is fine, but keep patterns simple.
121- Do not rely on `mapfile`, `readarray`, `coproc`, `printf -v`, or process substitution that requires `/dev/fd` (often missing on macOS).
122- Prefer `$( command )` subshells over backticks and quote every expansion.
123- Use `printf` instead of `echo -e` for reliable escape handling.
124
125### Usage Function Pattern
126```bash
127usage() {
128 cat <<'EOF'
129Usage: my_script.sh <source> <destination> [--dry-run]
130
131Required arguments:
132 source Path to the input file (must exist)
133 destination Output directory (will be created if missing)
134
135Environment variables:
136 API_TOKEN Token used to authenticate API requests
137
138External tools:
139 curl, jq
140
141Examples:
142 my_script.sh ./input.csv ./out --dry-run
143EOF
144}
145```
146Tailor the body to the specific script; keep instructions kind and explicit.
147
148### Script Assembly Checklist
1491. Write header, safety settings, and optional metadata comments.
1502. Define requirement arrays (even if empty) and defaults for optional values.
1513. Implement `usage()` and `check_requirements()` exactly once.
1524. Parse arguments safely (`getopts` or manual loop) and convert into named variables.
1535. Call `check_requirements` immediately after parsing. If it fails, exit with `exit 1`.
1546. Implement `main()` with clear, modular helpers; rely on functions instead of sprawling inline code.
1557. End with `main "$@"` and ensure the script returns appropriate exit codes.
156
157---
158
159## Phase 4 · Validate, Format, and Hand Off
1601. **Self-check**
161 - Does the script run without arguments and show `usage`?
162 - Do missing env vars and programs produce the friendly errors described earlier?
163 - Do all branches respect `set -euo pipefail` (guard nullable variables with `${VAR:-}`)?
164
1652. **Formatting via `shfmt`**
166 - Detect availability: `if command -v shfmt >/dev/null 2>&1; then ... fi`
167 - Run `shfmt -i 2 -bn -ci -sr -w <path-to-script>` after writing the file.
168 - Mention in your response whether formatting ran or was skipped (and why).
169
1703. **Final Response Checklist**
171 - Provide the complete script in a fenced code block (label it `bash`).
172 - Summarize how requirements are enforced.
173 - If manual formatting was necessary (no `shfmt`), note it explicitly.
174 - Suggest any quick validation commands (dry runs, linting) if relevant.
175
176---
177
178## Reference Template
179Use this skeleton as a starting point and adapt each section based on the user's requirements:
180
181```bash
182#!/usr/bin/env bash
183set -euo pipefail
184
185# Script: <name>
186# Purpose: <one-line description>
187# Requirements: <short summary of args/env/programs>
188
189REQUIRED_ARGS=("arg1" "arg2")
190REQUIRED_ENV_VARS=("ENV_VAR")
191REQUIRED_PROGRAMS=("curl" "jq")
192
193usage() {
194 cat <<'EOF'
195Usage: <script-name> <arg1> <arg2>
196
197Required arguments:
198 arg1 <describe>
199 arg2 <describe>
200
201Environment variables:
202 ENV_VAR <describe>
203
204External tools:
205 curl, jq
206EOF
207}
208
209check_requirements() {
210 local -r provided_arg_count=$1
211 local missing=0
212
213 if [ ${#REQUIRED_ARGS[@]} -gt 0 ] && [ "$provided_arg_count" -lt ${#REQUIRED_ARGS[@]} ]; then
214 printf 'Error: Expected %s arguments (%s) but received %s.\n' \
215 ${#REQUIRED_ARGS[@]} "${REQUIRED_ARGS[*]}" "$provided_arg_count" >&2
216 missing=1
217 fi
218
219 local env_var
220 for env_var in "${REQUIRED_ENV_VARS[@]}"; do
221 if [ -z "${!env_var:-}" ]; then
222 printf 'Error: Missing required environment variable %s. Please set it before rerunning.\n' "$env_var" >&2
223 missing=1
224 fi
225 done
226
227 local program
228 for program in "${REQUIRED_PROGRAMS[@]}"; do
229 if ! command -v "$program" >/dev/null 2>&1; then
230 printf 'Error: Required program %s is not installed or not on PATH. Please install it first.\n' "$program" >&2
231 missing=1
232 fi
233 done
234
235 if [ "$missing" -ne 0 ]; then
236 printf '\n' >&2
237 usage >&2
238 return 1
239 fi
240}
241
242parse_args() {
243 # TODO: replace with real parsing
244 SOURCE=${1:-}
245 DEST=${2:-}
246}
247
248main() {
249 parse_args "$@"
250 check_requirements "$#" || exit 1
251
252 # TODO: script logic goes here
253 printf 'Running with source=%s dest=%s\n' "$SOURCE" "$DEST"
254}
255
256main "$@"
257```
258
259Update placeholders, replace `TODO` sections, and adjust arrays when a requirement does not apply (leave the array empty—do not delete it).
260
261---
262
263## Quality Checklist Before Finishing
264- [ ] Script declares all requirement arrays and the `check_requirements` function
265- [ ] Error messages are friendly, specific, and routed to STDERR
266- [ ] Script avoids Bash ≥4 features and has been reviewed for 3.2 compatibility
267- [ ] `usage()` accurately reflects arguments, env vars, and dependencies
268- [ ] Formatting completed with `shfmt` (or explicitly noted why it was skipped)
269- [ ] Final response contains both summary guidance and the full script for copy/paste