<< All versions
Skill v1.0.1
currentAutomated scan100/100b33eep/claude-code-setup/standards-shell
3 files
──Details
PublishedMay 25, 2026 at 09:45 PM
Content Hashsha256:7dc5d1b469f98432...
Git SHA7e8cc42fb508
Bump Typepatch
──Files
Files (1 file, 8.4 KB)
SKILL.md8.4 KBactive
SKILL.md · 398 lines · 8.4 KB
version: "1.0.1" name: standards-shell description: This skill provides Shell/Bash coding standards and is automatically loaded for shell projects. It includes defensive scripting patterns, best practices, and recommended tooling. type: context applies_to: [bash, sh, shell, zsh, shellcheck, bats] file_extensions: [".sh", ".bash"]
Shell/Bash Coding Standards
Core Principles
- Simplicity: Simple, understandable scripts
- Readability: Readability over cleverness
- Maintainability: Scripts that are easy to maintain
- Testability: Scripts that are easy to test
- DRY: Don't Repeat Yourself - but don't overdo it
- Defensiveness: Fail early, fail loudly
General Rules
- Defensive Header: Always use
set -euo pipefail - Quote Variables: Always quote variables
"$var" - Descriptive Names: Meaningful names for variables and functions
- Minimal Changes: Only change relevant code parts
- No Over-Engineering: No unnecessary complexity
- ShellCheck Clean: All scripts must pass ShellCheck
Naming Conventions
| Element | Convention | Example | |
|---|---|---|---|
| Variables | snake_case | user_name, file_count | |
| Functions | snake_case | get_user_by_id, validate_input | |
| Constants | UPPER_SNAKE_CASE | MAX_RETRIES, DEFAULT_TIMEOUT | |
| Files | kebab-case or snake_case | deploy-app.sh, run_tests.sh | |
| Environment Vars | UPPER_SNAKE_CASE | API_URL, DATABASE_HOST |
Script Template
bash
#!/bin/bashset -euo pipefailreadonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"readonly SCRIPT_NAME="$(basename "${BASH_SOURCE[0]}")"# Cleanup on exitcleanup() {rm -f "$SCRIPT_DIR"/*.tmp 2>/dev/null || true}trap cleanup EXIT# Error handlererror_handler() {echo "Error on line $1" >&2exit 1}trap 'error_handler $LINENO' ERRmain() {# Script logic hereecho "Running $SCRIPT_NAME"}main "$@"
Defensive Scripting
bash
# REQUIRED: Always start with thisset -euo pipefail# -e: Exit on error# -u: Error on undefined variables# -o pipefail: Pipe fails if any command fails# RECOMMENDED: Safer IFSIFS=$'\n\t'# REQUIRED: Quote all variablesecho "$var" # Goodecho $var # Bad - word splitting# REQUIRED: Use [[ ]] for conditionals (Bash)if [[ -f "$file" ]]; then # Goodif [ -f "$file" ]; then # POSIX only
Parameter Expansion
bash
# Defaults and validation${var:-default} # Use default if unset${var:=default} # Assign default if unset${var:?error message} # Error if unset# String manipulation${var#pattern} # Remove prefix (shortest)${var##pattern} # Remove prefix (longest)${var%pattern} # Remove suffix (shortest)${var%%pattern} # Remove suffix (longest)${var/old/new} # Replace first${var//old/new} # Replace all${#var} # Length# Examplesfile="document.txt"echo "${file%%.*}" # "document" (remove extension)echo "${file##*.}" # "txt" (get extension)
Functions
bash
# REQUIRED: Use local variablesget_user_name() {local user_id=$1local namename=$(grep "^${user_id}:" /etc/passwd | cut -d: -f5)echo "$name"}# Return values via stdoutresult=$(get_user_name "1000")# Return status codesvalidate_file() {local file=$1if [[ ! -f "$file" ]]; thenecho "Error: File not found: $file" >&2return 1fireturn 0}if validate_file "$input_file"; thenprocess_file "$input_file"fi
Arrays
bash
# Indexed arraysfiles=("file1.txt" "file2.txt" "file3.txt")echo "${files[0]}" # First elementecho "${files[@]}" # All elementsecho "${#files[@]}" # Array length# Iterate safelyfor file in "${files[@]}"; doecho "Processing: $file"done# Associative arrays (Bash 4+)declare -A configconfig[host]="localhost"config[port]="8080"echo "${config[host]}:${config[port]}"
File Operations
bash
# Read file line by linewhile IFS= read -r line; doecho "Line: $line"done < "input.txt"# Read into arraymapfile -t lines < "input.txt"# Write to file (heredoc)cat > output.txt <<EOFLine 1Line 2EOF# Temp files with cleanuptemp_file=$(mktemp)trap 'rm -f "$temp_file"' EXIT
Error Handling
bash
# Trap for cleanupcleanup() {echo "Cleaning up..."rm -f "$temp_file"}trap cleanup EXIT# Trap for errorserror_handler() {local line=$1echo "Error occurred on line $line" >&2}trap 'error_handler $LINENO' ERR# Check command existsif ! command -v python3 &>/dev/null; thenecho "Error: python3 not found" >&2exit 1fi# Conditional executioncommand1 && command2 # Run command2 only if command1 succeedscommand1 || command2 # Run command2 only if command1 fails
Argument Parsing with getopts
bash
usage() {echo "Usage: $0 [-v] [-o output] [-h]"echo " -v Verbose mode"echo " -o FILE Output file"echo " -h Show help"exit 1}verbose=falseoutput_file=""while getopts "vo:h" opt; docase $opt inv) verbose=true ;;o) output_file="$OPTARG" ;;h) usage ;;*) usage ;;esacdoneshift $((OPTIND - 1))# Remaining args in $@
Logging
bash
log() {local level=$1shiftecho "[$(date '+%Y-%m-%d %H:%M:%S')] [$level] $*" >&2}log_info() { log "INFO" "$@"; }log_warn() { log "WARN" "$@"; }log_error() { log "ERROR" "$@"; }# Usagelog_info "Starting process"log_error "Failed to connect"
Debugging
bash
# Enable debuggingset -x # Print commandsPS4='+ ${BASH_SOURCE}:${LINENO}: ' # Better debug output# Debug specific sectionset -x# code to debugset +x# Run script with debugbash -x script.shbash -n script.sh # Syntax check only
Common Patterns
bash
# Check if rootif [[ $EUID -ne 0 ]]; thenecho "This script must be run as root" >&2exit 1fi# Safe directory changecd "$target_dir" || exit 1# Process files safely (handles spaces)find . -name "*.txt" -print0 | while IFS= read -r -d '' file; doecho "Processing: $file"done# Retry patternretry() {local max_attempts=$1local delay=$2shift 2local attempt=1while [[ $attempt -le $max_attempts ]]; doif "$@"; thenreturn 0filog_warn "Attempt $attempt failed, retrying in ${delay}s..."sleep "$delay"((attempt++))donereturn 1}retry 3 5 curl -f "https://api.example.com/health"
Recommended Tooling
| Tool | Purpose | |
|---|---|---|
shellcheck | Static analysis (required) | |
shfmt | Code formatting | |
bats-core | Testing framework | |
bash 5.x | Modern features (avoid macOS default 3.2) |
ShellCheck Usage
bash
# Run ShellCheckshellcheck script.sh# Disable specific warning (sparingly)# shellcheck disable=SC2086echo $UNQUOTED_VAR# Follow sourced filesshellcheck -x script.sh
Testing with bats-core
bash
#!/usr/bin/env bats# File: test_script.batssource ./script.sh@test "add function returns correct sum" {result=$(add 5 3)[ "$result" = "8" ]}@test "validate_file fails on missing file" {run validate_file "nonexistent.txt"[ "$status" -eq 1 ]}
Run tests:
bash
bats tests/
POSIX Compatibility
For maximum portability (sh, dash, ash):
sh
#!/bin/sh# Use [ ] instead of [[ ]]if [ -f "file.txt" ]; thenecho "File exists"fi# No arrays, use positional parametersset -- "apple" "banana" "cherry"echo "First: $1"# No $() in older shells, use backtickscurrent_date=`date +%Y-%m-%d`
Production Best Practices
- Defensive header - Always
set -euo pipefail - Quote everything - Prevent word splitting and glob expansion
- Local variables - Use
localin functions - ShellCheck clean - No warnings before commit
- Cleanup traps - Always clean up temp files
- Meaningful exit codes - 0 for success, non-zero for errors
- Logging to stderr - Keep stdout for data, stderr for logs
- Check dependencies - Verify required commands exist
- Handle signals - Trap SIGTERM for graceful shutdown
- Document usage - Include
--helpoption
References
- Based on moai-lang-shell by AJBcoding