shell-scripting
npx machina-cli add skill ancoleman/ai-design-components/shell-scripting --openclawShell Scripting
Purpose
Provides patterns and best practices for writing maintainable shell scripts with error handling, argument parsing, and portability considerations. Covers POSIX sh vs Bash decision-making, parameter expansion, integration with common utilities (jq, yq, awk), and testing with ShellCheck and Bats.
When to Use This Skill
Use shell scripting when:
- Orchestrating existing command-line tools and system utilities
- Writing CI/CD pipeline scripts (GitHub Actions, GitLab CI)
- Creating container entrypoints and initialization scripts
- Automating system administration tasks (backups, log rotation)
- Building development tooling (build scripts, test runners)
Consider Python/Go instead when:
- Complex business logic or data structures required
- Cross-platform GUI needed
- Heavy API integration (REST, gRPC)
- Script exceeds 200 lines with significant logic complexity
POSIX sh vs Bash
Use POSIX sh (#!/bin/sh) when:
- Maximum portability required (Linux, macOS, BSD, Alpine)
- Minimal container images needed
- Embedded systems or unknown target environments
Use Bash (#!/bin/bash) when:
- Controlled environment (specific OS, container)
- Arrays or associative arrays needed
- Advanced parameter expansion beneficial
- Process substitution
<(cmd)useful
For detailed comparison and testing strategies, see references/portability-guide.md.
Essential Error Handling
Fail-Fast Pattern
#!/bin/bash
set -euo pipefail
# -e: Exit on error
# -u: Exit on undefined variable
# -o pipefail: Pipeline fails if any command fails
Use for production automation, CI/CD scripts, and critical operations.
Explicit Exit Code Checking
#!/bin/bash
if ! command_that_might_fail; then
echo "Error: Command failed" >&2
exit 1
fi
Use for custom error messages and interactive scripts.
Trap Handlers for Cleanup
#!/bin/bash
set -euo pipefail
TEMP_FILE=$(mktemp)
cleanup() {
rm -f "$TEMP_FILE"
}
trap cleanup EXIT
Use for guaranteed cleanup of temporary files, locks, and resources.
For comprehensive error patterns, see references/error-handling.md.
Argument Parsing
Short Options with getopts (POSIX)
#!/bin/bash
while getopts "hvf:o:" opt; do
case "$opt" in
h) usage ;;
v) VERBOSE=true ;;
f) INPUT_FILE="$OPTARG" ;;
o) OUTPUT_FILE="$OPTARG" ;;
*) usage ;;
esac
done
shift $((OPTIND - 1))
Long Options (Manual Parsing)
#!/bin/bash
while [[ $# -gt 0 ]]; do
case "$1" in
--help) usage ;;
--verbose) VERBOSE=true; shift ;;
--file) INPUT_FILE="$2"; shift 2 ;;
--file=*) INPUT_FILE="${1#*=}"; shift ;;
*) break ;;
esac
done
For hybrid approaches and validation patterns, see references/argument-parsing.md.
Parameter Expansion Quick Reference
# Default values
${var:-default} # Use default if unset
${var:=default} # Assign default if unset
: "${API_KEY:?Error: required}" # Error if unset
# String manipulation
${#var} # String length
${var:offset:length} # Substring
${var%.txt} # Remove suffix
${var##*/} # Basename
${var/old/new} # Replace first
${var//old/new} # Replace all
# Case conversion (Bash 4+)
${var^^} # Uppercase
${var,,} # Lowercase
For complete expansion patterns and array handling, see references/parameter-expansion.md.
Common Utilities Integration
JSON with jq
# Extract field
name=$(curl -sSL https://api.example.com/user | jq -r '.name')
# Filter array
active=$(jq '.users[] | select(.active) | .name' data.json)
# Check existence
if ! echo "$json" | jq -e '.field' >/dev/null; then
echo "Error: Field missing" >&2
fi
YAML with yq
# Read value (yq v4)
host=$(yq eval '.database.host' config.yaml)
# Update in-place
yq eval '.port = 5432' -i config.yaml
# Convert to JSON
yq eval -o=json config.yaml
Text Processing
# awk: Extract columns
awk -F',' '{print $1, $3}' data.csv
# sed: Replace text
sed 's/old/new/g' file.txt
# grep: Pattern match
grep -E "ERROR|WARN" logfile.txt
For detailed examples and best practices, see references/common-utilities.md.
Testing and Validation
ShellCheck: Static Analysis
# Check script
shellcheck script.sh
# POSIX compliance
shellcheck --shell=sh script.sh
# Exclude warnings
shellcheck --exclude=SC2086 script.sh
Bats: Automated Testing
#!/usr/bin/env bats
@test "script runs successfully" {
run ./script.sh --help
[ "$status" -eq 0 ]
[ "${lines[0]}" = "Usage: script.sh [OPTIONS]" ]
}
@test "handles missing argument" {
run ./script.sh
[ "$status" -eq 1 ]
[[ "$output" =~ "Error" ]]
}
Run tests:
bats test/
For CI/CD integration and debugging techniques, see references/testing-guide.md.
Defensive Programming Checklist
#!/bin/bash
set -euo pipefail
# Check required commands
command -v jq >/dev/null 2>&1 || {
echo "Error: jq required" >&2
exit 1
}
# Check environment variables
: "${API_KEY:?Error: API_KEY required}"
# Check files
[ -f "$CONFIG_FILE" ] || {
echo "Error: Config not found: $CONFIG_FILE" >&2
exit 1
}
# Quote all variables
echo "Processing: $file" # ❌ Unquoted
echo "Processing: \"$file\"" # ✅ Quoted
Platform Considerations
macOS vs Linux Differences
# sed in-place
sed -i '' 's/old/new/g' file.txt # macOS
sed -i 's/old/new/g' file.txt # Linux
# Portable: Use temp file
sed 's/old/new/g' file.txt > file.txt.tmp
mv file.txt.tmp file.txt
# readlink
readlink -f /path # Linux only
cd "$(dirname "$0")" && pwd # Portable
For complete platform differences, see references/portability-guide.md.
Script Categories
System Administration: Cron jobs, log rotation, backup automation Build/Deployment: CI/CD pipelines, Docker builds, deployments Development Tooling: Project setup, test runners, code generators Container Entrypoints: Initialization, signal handling, configuration
Production Script Template
#!/bin/bash
set -euo pipefail
readonly SCRIPT_NAME="$(basename "$0")"
readonly SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
TEMP_DIR=""
cleanup() {
local exit_code=$?
rm -rf "$TEMP_DIR"
exit "$exit_code"
}
trap cleanup EXIT
log() {
echo "[$(date +'%Y-%m-%d %H:%M:%S')] $*" >&2
}
main() {
# Check dependencies
command -v jq >/dev/null 2>&1 || exit 1
# Parse arguments
# Validate input
# Process
# Report results
log "Completed successfully"
}
main "$@"
For complete production template, see examples/production-template.sh.
Tool Recommendations
Core Tools:
- jq: JSON parsing and transformation
- yq: YAML parsing (v4 recommended)
- ShellCheck: Static analysis and linting
- Bats: Automated testing framework
Installation:
# macOS
brew install jq yq shellcheck bats-core
# Ubuntu/Debian
apt-get install jq shellcheck
Related Skills
- linux-administration: System commands and administration
- building-ci-pipelines: Using scripts in CI/CD
- infrastructure-as-code: Terraform/Pulumi wrappers
- kubernetes-operations: kubectl scripts, Helm hooks
- writing-dockerfiles: Container entrypoints
Additional Resources
Reference Files:
references/error-handling.md- Comprehensive error patternsreferences/argument-parsing.md- Advanced parsing techniquesreferences/parameter-expansion.md- Complete expansion referencereferences/portability-guide.md- POSIX vs Bash differencesreferences/testing-guide.md- ShellCheck and Bats guidereferences/common-utilities.md- jq, yq, awk, sed usage
Example Scripts:
examples/production-template.sh- Production-ready templateexamples/getopts-basic.sh- Simple getopts usageexamples/getopts-advanced.sh- Complex option handlingexamples/long-options.sh- Manual long option parsingexamples/error-handling.sh- Error handling patternsexamples/json-yaml-processing.sh- jq/yq examples
Utility Scripts:
scripts/lint-script.sh- ShellCheck wrapper for CIscripts/test-script.sh- Bats wrapper for CI
Source
git clone https://github.com/ancoleman/ai-design-components/blob/main/skills/shell-scripting/SKILL.mdView on GitHub Overview
Shell Scripting provides patterns and best practices for writing maintainable shell scripts with proper error handling, argument parsing, and portability considerations. It covers when to use POSIX sh vs Bash, parameter expansion, integration with tools like jq and awk, and testing with ShellCheck and Bats.
How This Skill Works
Scripts adopt strict modes (set -euo pipefail), explicit exit code checks, and trap-based cleanup. They demonstrate POSIX short option parsing with getopts and manual parsing for long options, portable parameter expansion, and seamless integration with utilities like jq, yq, and awk, plus testing workflows with ShellCheck and Bats.
When to Use It
- Orchestrating existing command-line tools and system utilities
- Writing CI/CD pipeline scripts (GitHub Actions, GitLab CI)
- Creating container entrypoints and initialization scripts
- Automating system administration tasks (backups, log rotation)
- Building development tooling (build scripts, test runners)
Quick Start
- Step 1: Decide between POSIX sh or Bash based on target environments and features needed
- Step 2: Enable strict mode with set -euo pipefail and add a trap for cleanup
- Step 3: Implement argument parsing (getopts for short options, manual parsing for long options) and test with ShellCheck
Best Practices
- Prefer POSIX sh for maximum portability; use #!/bin/sh, and reserve Bash when environment is controlled
- Enable strict mode: set -euo pipefail and use clear, descriptive error messages
- Use getopts for short options and a robust manual parser for long options
- Implement trap handlers for cleanup of temporary files, locks, and resources
- Validate inputs, keep functions small, and test with ShellCheck and Bats
Example Use Cases
- Portable backup script that uses rsync and tar with fail-fast checks and clear error messages
- Container entrypoint that initializes environment, waits for services, and execs the provided command
- CI/CD wrapper that installs dependencies and runs tests across multiple steps
- CLI tool that accepts --file or --file=PATH and processes JSON with jq
- Log rotation helper that compresses old logs and trims retention safely