| name | bash-dev |
| description | Bash scripting development standards, error handling, and best practices. Activated when working with .sh files or Bash scripts. |
| allowed-tools | Read, Grep, Bash |
Bash Development Expert
This skill supports Bash script development with best practices and safety standards.
🎯 Core Rules
Shebang and Safety
- Shebang: Always use
#!/usr/bin/env bash - Set Options: Always use
set -euo pipefail-e: Exit on error-u: Exit on undefined variable-o pipefail: Fail pipeline if any command fails
Variable Handling
- Quoting: Always quote variables
"${var}" - Constants: Use UPPERCASE for global/environment variables
- Local Variables: Use lowercase for function-local variables
- Readonly: Use
readonlyfor constants
Function Best Practices
- Local Variables: Always use
localkeyword - Parameter Validation: Validate required parameters
- Return Codes: 0 for success, non-zero for errors
- Documentation: Document usage, description, and return codes
📚 Script Template
Standard Script Structure
#!/usr/bin/env bash
set -euo pipefail
# Script metadata
# Usage: script_name.sh <arg1> [arg2]
# Description: What this script does
# Author: Your Name
# Version: 1.0.0
# Global constants (UPPERCASE)
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly SCRIPT_NAME="$(basename "${BASH_SOURCE[0]}")"
readonly VERSION="1.0.0"
# Color codes for output
readonly RED='\033[0;31m'
readonly GREEN='\033[0;32m'
readonly YELLOW='\033[1;33m'
readonly NC='\033[0m' # No Color
#######################################
# Display usage information
# Globals:
# SCRIPT_NAME
# Arguments:
# None
# Outputs:
# Writes usage to stdout
#######################################
usage() {
cat <<EOF
Usage: ${SCRIPT_NAME} <arg1> [arg2]
Description:
What this script does in detail
Arguments:
arg1 Required argument description
arg2 Optional argument description (default: value)
Options:
-h, --help Show this help message
-v, --version Show version information
Examples:
${SCRIPT_NAME} value1
${SCRIPT_NAME} value1 value2
EOF
}
#######################################
# Print error message and exit
# Globals:
# RED, NC
# Arguments:
# Error message
# Outputs:
# Writes error to stderr
# Returns:
# 1 (error code)
#######################################
error() {
echo -e "${RED}Error: $*${NC}" >&2
exit 1
}
#######################################
# Print info message
# Globals:
# GREEN, NC
# Arguments:
# Info message
# Outputs:
# Writes message to stdout
#######################################
info() {
echo -e "${GREEN}Info: $*${NC}"
}
#######################################
# Print warning message
# Globals:
# YELLOW, NC
# Arguments:
# Warning message
# Outputs:
# Writes warning to stdout
#######################################
warn() {
echo -e "${YELLOW}Warning: $*${NC}"
}
#######################################
# Main function
# Globals:
# None
# Arguments:
# Command line arguments
# Returns:
# 0 on success, 1 on error
#######################################
main() {
local arg1="${1:?Error: arg1 required}"
local arg2="${2:-default_value}"
info "Processing: ${arg1}"
# Your logic here
info "Completed successfully"
return 0
}
# Error handler
trap 'error "Script failed on line $LINENO"' ERR
# Parse arguments
case "${1:-}" in
-h|--help)
usage
exit 0
;;
-v|--version)
echo "${SCRIPT_NAME} version ${VERSION}"
exit 0
;;
esac
# Execute main function
main "$@"
🛠️ Common Patterns
Error Handling
# Check if command exists
if ! command -v git &> /dev/null; then
error "git is not installed"
fi
# Check if file exists
if [[ ! -f "${config_file}" ]]; then
error "Config file not found: ${config_file}"
fi
# Check if directory exists
if [[ ! -d "${target_dir}" ]]; then
mkdir -p "${target_dir}" || error "Failed to create directory"
fi
Input Validation
# Validate required argument
validate_arg() {
local arg="${1:?Error: argument required}"
if [[ -z "${arg}" ]]; then
error "Argument cannot be empty"
fi
}
# Validate number
validate_number() {
local num="$1"
if ! [[ "${num}" =~ ^[0-9]+$ ]]; then
error "Invalid number: ${num}"
fi
}
File Operations
# Safe file read
read_file() {
local file="$1"
if [[ ! -r "${file}" ]]; then
error "Cannot read file: ${file}"
fi
cat "${file}"
}
# Safe file write
write_file() {
local file="$1"
local content="$2"
echo "${content}" > "${file}" || error "Failed to write file"
}
Array Handling
# Declare array
declare -a items=("item1" "item2" "item3")
# Iterate over array
for item in "${items[@]}"; do
echo "Processing: ${item}"
done
# Array length
echo "Total items: ${#items[@]}"
🎯 Quality Checklist
Check these before committing:
- Shebang
#!/usr/bin/env bashpresent -
set -euo pipefailat the beginning - All variables quoted
"${var}" - Global variables in UPPERCASE
- Local variables use
localkeyword - Functions have documentation comments
- Error handling implemented
- Usage function provided
- Exit codes are meaningful (0=success, non-zero=error)
- Script tested with
shellcheck
🔍 Common Anti-patterns to Avoid
❌ Don't:
# Unquoted variables
cd $HOME/dir
# Missing error handling
mkdir /some/dir
# Undefined variables
echo $UNDEFINED_VAR
# No set options
#!/bin/bash
✅ Do:
# Quoted variables
cd "${HOME}/dir" || error "Failed to change directory"
# With error handling
mkdir -p "${target_dir}" || error "Failed to create directory"
# Check before use
if [[ -n "${VAR:-}" ]]; then
echo "${VAR}"
fi
# Proper set options
#!/usr/bin/env bash
set -euo pipefail
💡 Testing
Using ShellCheck
# Install shellcheck
brew install shellcheck # macOS
# Check script
shellcheck script.sh
# Ignore specific warnings
# shellcheck disable=SC2086
command ${unquoted}
Testing Functions
# Simple test function
test_function() {
local expected="expected_value"
local actual
actual="$(your_function)"
if [[ "${actual}" != "${expected}" ]]; then
error "Test failed: expected '${expected}', got '${actual}'"
fi
info "Test passed"
}