Claude-skill-registry claude-skill-bash
Apply comprehensive bash scripting standards including main function pattern, usage documentation, argument parsing, dependency checking, and error handling. Triggers when creating/editing .sh files, bash scripts, or discussing shell scripting, deployment scripts, automation tasks, or bash conventions.
git clone https://github.com/majiayu000/claude-skill-registry
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/claude-skill-bash" ~/.claude/skills/majiayu000-claude-skill-registry-claude-skill-bash && rm -rf "$T"
skills/data/claude-skill-bash/SKILL.mdBash Best Practices Skill
This skill ensures all bash scripts follow enterprise-grade best practices for maintainability, reliability, and user-friendliness.
When This Skill Applies
This skill should be automatically invoked when:
- Creating new bash scripts or shell scripts
- Editing existing .sh or .bash files
- User requests scripts for automation, deployment, backup, or system tasks
- Reviewing or refactoring bash code
- User mentions: bash, shell script, main function, argument parsing, script structure
Script Complexity Guidelines
This skill recognizes two categories of scripts with different structural requirements:
Simple Scripts
Scripts that do one simple thing well without unnecessary ceremony.
Characteristics:
- Less than ~30 lines of actual logic (excluding comments and whitespace)
- No command-line arguments required
- Single, focused purpose that's self-evident
- Linear flow or minimal branching
- Output is often consumed by other programs or scripts
Examples: Version detection, environment checks, data format conversion, simple calculations
Structural Requirements:
- Main function and guard clause: Optional
- Usage function: Not needed (no arguments to document)
- Argument parsing: Not applicable
- Color output: Optional (often unnecessary)
- Can use direct execution without function wrapping
Ordinary Scripts
Scripts with broader scope requiring full structure for maintainability.
Characteristics:
- More than ~30 lines of logic OR
- Takes command-line arguments OR
- Multiple functions or complex branching OR
- Interactive use by humans OR
- Requires detailed documentation
Examples: Deployment tools, backup utilities, system maintenance scripts, development tools
Structural Requirements:
- Main function and guard clause: Required
- Usage function: Required
- Argument parsing: Required (if taking arguments)
- Full error handling: Required
- User-friendly output: Required (for interactive scripts)
Core Principles to Apply
For Ordinary Scripts
When writing ordinary scripts, ensure:
- Main function pattern with guard clause - Every script must have a main() function
- Usage function - Clear help documentation
- Argument parsing - Structured option handling in main
- Dependency checking - Explicit validation of required tools
- Explicit error handling - Never use
, handle errors explicitlyset -e - Function organization - Correct ordering and single responsibility
- User-friendly output - Colored, structured output with status indicators
For Simple Scripts
When writing simple scripts, ensure:
- Clear purpose comment - Explain what the script does at the top
- Explicit error handling - Never use
, handle errors explicitlyset -e - Proper exit codes - 0 for success, non-zero for failure
- Error output to stderr - Use
for error messages>&2 - Variable safety - Quote variables, handle undefined cases
Script Structure Templates
Template for Ordinary Scripts
Every new bash script should follow this structure:
#!/usr/bin/env bash # Global declarations DEPENDENCIES=(jq curl git) # List external tools needed SCRIPT_NAME=$(basename "$0") VERSION="1.0.0" # Color definitions (if outputting to terminal) RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # Allow users to disable colors if [[ -n "${NO_COLOR:-}" ]] || [[ "${TERM:-}" == "dumb" ]]; then RED="" GREEN="" YELLOW="" BLUE="" NC="" fi function usage() { cat <<EOM Brief description of what this script does. usage: ${SCRIPT_NAME} [options] options: -i|--input <file> Input file to process (required) -o|--output <file> Output file (optional, defaults to stdout) -v|--verbose Enable verbose output -h|--help Show this help message --version Show version information dependencies: ${DEPENDENCIES[@]} examples: ${SCRIPT_NAME} -i data.txt -o report.json ${SCRIPT_NAME} --input data.txt --verbose EOM exit 1 } function main() { # Default values local input_file="" local output_file="" local verbose=false # Parse arguments while [ "$1" != "" ]; do case $1 in -i | --input) shift input_file="$1" ;; -o | --output) shift output_file="$1" ;; -v | --verbose) verbose=true ;; --version) echo "${SCRIPT_NAME} version ${VERSION}" exit 0 ;; -h | --help) usage ;; *) echo "Error: Unknown option '$1'" usage ;; esac shift done # Validate required arguments if [ -z "$input_file" ]; then echo "Error: Input file is required" >&2 usage fi # Check dependencies exit_on_missing_tools "${DEPENDENCIES[@]}" # Main script logic process_file "$input_file" "$output_file" "$verbose" } # Business logic functions function process_file() { local input="$1" local output="$2" local verbose="$3" # Implementation here echo "Processing ${input}..." } # Utility functions function exit_on_missing_tools() { for cmd in "$@"; do if command -v "$cmd" &>/dev/null; then continue fi printf "Error: Required tool '%s' is not installed or not in PATH\n" "$cmd" exit 1 done } # Guard clause - only execute main if script is run directly if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then main "$@" exit 0 fi
Template for Simple Scripts
For scripts that do one simple thing without arguments:
#!/usr/bin/env bash # Purpose: Determine version for CI builds based on git state # Usage: determine-version.sh # Output: Version string to stdout # For simple scripts, direct execution is fine - no main() required # Check if we're on a tag TAG=$(git describe --exact-match --tags HEAD 2>/dev/null) if [ -n "$TAG" ]; then # On a tag - use the tag as version echo "${TAG#v}" else # Not on tag - determine from branch if [ -n "$GITHUB_HEAD_REF" ]; then # Pull request BRANCH="$GITHUB_HEAD_REF" elif [ -n "$GITHUB_REF_NAME" ]; then # Push to branch BRANCH="$GITHUB_REF_NAME" else # Fallback to current branch BRANCH=$(git symbolic-ref -q --short HEAD 2>/dev/null) if [ -z "$BRANCH" ]; then echo "Error: Unable to determine branch" >&2 exit 1 fi fi # Sanitize branch name for version string SANITIZED=$(echo "$BRANCH" | sed 's/[^a-zA-Z0-9._-]/_/g') echo "${SANITIZED}-SNAPSHOT" fi
Key points for simple scripts:
- Clear purpose comment at the top
- Direct execution without main() wrapper
- Errors still go to stderr with >&2
- Meaningful exit codes (0 for success, non-zero for failure)
- Variables are still properly quoted
- No need for usage() function when there are no arguments
- No need for color output when output is data
Detailed Best Practices
1. Script Bootstrapping
Main Function Pattern
- ALWAYS use a
function as the entry pointmain - This enables safe sourcing by other scripts
- Provides clear structure and testability
The Guard Clause
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then main "$@" exit 0 fi
- Must be at the very end of the script
- Enables the script to be sourced without execution
- Always include explicit
after mainexit 0
2. Usage Function and Argument Parsing
Usage Function Requirements
- Must be defined before main() function
- Use heredoc for clean formatting
- Include: description, options, dependencies, examples
- Exit with status 1
Argument Parsing Best Practices
function main() { # Set defaults first local input_file="" local verbose=false # Parse with while loop and case while [ "$1" != "" ]; do case $1 in -i | --input) shift input_file="$1" ;; -v | --verbose) verbose=true ;; -h | --help) usage ;; *) echo "Error: Unknown option '$1'" usage ;; esac shift done # Validate after parsing if [ -z "$input_file" ]; then echo "Error: Input file is required" >&2 usage fi }
3. Dependency Declaration and Checking
Always Declare Dependencies
DEPENDENCIES=(jq curl git docker)
Check Before Use
function exit_on_missing_tools() { for cmd in "$@"; do if command -v "$cmd" &>/dev/null; then continue fi printf "Error: Required tool '%s' is not installed or not in PATH\n" "$cmd" exit 1 done }
Include in Usage
Show dependencies in the usage function so users know requirements upfront.
4. Function Organization
Correct Function Order (MANDATORY)
- First after global declarationsusage()
- Immediately after usagemain()- Business logic functions - Core functionality
- Utility functions - Generic helpers
NEVER add section comment headers like "# UTILITY FUNCTIONS". The code structure speaks for itself.
Function Design Principles
- Single Responsibility: Each function does one thing
- Clear Inputs/Outputs: Use local variables, return meaningful status
- Descriptive Names: Use verb-noun combinations (create_backup, validate_input)
- Keep Small: Break down functions longer than 50 lines
- Use Local Variables: Always declare with
local
5. Error Handling
AVOID set -e
set -eNever use
set -e. Instead, use explicit error handling:
# Good - Explicit handling if ! command; then echo "Error: Command failed" >&2 exit 1 fi # Good - Chain with || cd /some/directory || { echo "Error: Cannot change directory" >&2 exit 1 } # Good - Capture and check output=$(command 2>&1) if [ $? -ne 0 ]; then echo "Error: Command failed: $output" >&2 exit 1 fi
Error Handling Rules
- Always redirect errors to stderr with
>&2 - Provide context about what failed
- Use meaningful exit codes
- Clean up temporary files on failure
6. User-Friendly Output
Color Output (When Appropriate)
# Define colors at script top RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # Respect NO_COLOR environment if [[ -n "${NO_COLOR:-}" ]] || [[ "${TERM:-}" == "dumb" ]]; then RED="" GREEN="" YELLOW="" BLUE="" NC="" fi
Output Functions
function print_header() { echo -e "${BLUE}=======================${NC}" echo -e "${BLUE}$1${NC}" echo -e "${BLUE}=======================${NC}" } function print_success() { echo -e "${GREEN}✅ $1${NC}" } function print_error() { echo -e "${RED}❌ Error: $1${NC}" >&2 } function print_warning() { echo -e "${YELLOW}⚠️ Warning: $1${NC}" } function print_step() { local current="$1" local total="$2" local message="$3" echo -e "${YELLOW}[${current}/${total}] ${message}${NC}" }
7. Keep Main Function Lean
Main should only:
- Parse arguments
- Validate inputs
- Check dependencies
- Call business functions
- Handle high-level errors
Business logic belongs in separate functions.
8. Code Comments
Avoid Excessive Comments
- Well-named functions are self-documenting
- Only comment the "why", not the "what"
- Use comments for:
- Complex algorithms
- Non-obvious workarounds
- Business logic rules
- External API quirks
# Bad - Obvious comment # Increment counter counter=$((counter + 1)) # Good - Explains why # Exponential backoff with jitter to prevent thundering herd delay=$((2 ** attempt * 1000 + RANDOM % 1000))
Script Generation Checklist
When generating a new script, verify:
- Shebang line:
#!/usr/bin/env bash - DEPENDENCIES array declared
- usage() function defined first
- main() function handles argument parsing
- Dependency checking with exit_on_missing_tools
- Guard clause at script end
- Explicit error handling (no
)set -e - Functions properly ordered
- Local variables in functions
- Errors redirected to stderr
- Meaningful exit codes
- Color output with NO_COLOR support (if applicable)
Common Patterns
Interactive Prompts
# Yes/no confirmation read -p "Continue? (y/n): " -n 1 -r echo if [[ ! $REPLY =~ ^[Yy]$ ]]; then echo "Operation cancelled" exit 0 fi # Menu selection echo "Select an option:" echo "1) Full backup" echo "2) Incremental backup" read -p "Enter choice (1-2): " choice # Password (hidden) read -s -p "Enter password: " password echo # Add newline
Temporary Files
# Create temp file safely temp_file=$(mktemp) trap "rm -f '$temp_file'" EXIT # Use temp directory temp_dir=$(mktemp -d) trap "rm -rf '$temp_dir'" EXIT
Pipeline Error Handling
# Without set -e, check PIPESTATUS command1 | command2 | command3 if [ "${PIPESTATUS[0]}" -ne 0 ]; then echo "Error: command1 failed" >&2 exit 1 fi
Examples to Follow
When users request scripts for common tasks, apply these patterns:
Backup Script
- Include timestamp in backup names
- Verify source exists before backup
- Check available disk space
- Provide progress feedback
- Clean up old backups based on retention
Deployment Script
- Validate environment first
- Show clear step progression
- Roll back on failure
- Log all operations
- Provide summary at end
Data Processing Script
- Validate input format
- Handle large files efficiently
- Provide progress indicators
- Support resume on failure
- Generate detailed reports
Choosing Between Simple and Ordinary Scripts
Decision Tree
Ask these questions to determine which pattern to use:
-
Does the script take command-line arguments?
- YES → Use ordinary script pattern (needs usage() and parsing)
- NO → Continue to question 2
-
Is the logic more than ~30 lines (excluding comments)?
- YES → Use ordinary script pattern (needs structure)
- NO → Continue to question 3
-
Does it have multiple functions or complex branching?
- YES → Use ordinary script pattern (needs organization)
- NO → Continue to question 4
-
Will it be run interactively by humans who need help?
- YES → Use ordinary script pattern (needs user-friendly features)
- NO → Continue to question 5
-
Is it doing one simple, self-evident task?
- YES → Use simple script pattern
- NO → Use ordinary script pattern
When in doubt, use the ordinary pattern. It's better to have structure you don't need than to need structure you don't have.
Examples of Simple Scripts
- Outputs version string based on git statedetermine-version.sh
- Verifies environment variables are setcheck-env.sh
- Reformats JSON from stdin to stdoutformat-json.sh
- Counts lines in specified file typescount-lines.sh
- Returns 0 if on production branchis-production.sh
Examples of Ordinary Scripts
- Multi-step deployment with rollbackdeploy.sh
- Database backup with retention managementbackup.sh
- Developer environment initializationsetup-dev.sh
- Configurable test executiontest-runner.sh
- Data migration with validationmigrate-data.sh
Testing Scripts
Always test generated scripts for:
- Syntax:
bash -n script.sh - Undefined variables: Run with
temporarilyset -u - Dependencies: Run without required tools installed
- Error paths: Test with invalid inputs
- Sourcing: Verify guard clause works (for ordinary scripts)
Remember
Every script you generate or modify should be production-ready:
- Maintainable by others
- Self-documenting through good structure
- Gracefully handles errors
- Provides helpful output
- Follows consistent patterns
These practices ensure scripts are professional, reliable, and user-friendly.