Install
$ agentstack add skill-b33eep-claude-code-setup-standards-shell ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
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 | snakecase | user_name, file_count | | Functions | snakecase | get_user_by_id, validate_input | | Constants | UPPERSNAKECASE | MAX_RETRIES, DEFAULT_TIMEOUT | | Files | kebab-case or snakecase | deploy-app.sh, run_tests.sh | | Environment Vars | UPPERSNAKE_CASE | API_URL, DATABASE_HOST |
Script Template
#!/bin/bash
set -euo pipefail
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly SCRIPT_NAME="$(basename "${BASH_SOURCE[0]}")"
# Cleanup on exit
cleanup() {
rm -f "$SCRIPT_DIR"/*.tmp 2>/dev/null || true
}
trap cleanup EXIT
# Error handler
error_handler() {
echo "Error on line $1" >&2
exit 1
}
trap 'error_handler $LINENO' ERR
main() {
# Script logic here
echo "Running $SCRIPT_NAME"
}
main "$@"
Defensive Scripting
# REQUIRED: Always start with this
set -euo pipefail
# -e: Exit on error
# -u: Error on undefined variables
# -o pipefail: Pipe fails if any command fails
# RECOMMENDED: Safer IFS
IFS=$'\n\t'
# REQUIRED: Quote all variables
echo "$var" # Good
echo $var # Bad - word splitting
# REQUIRED: Use [[ ]] for conditionals (Bash)
if [[ -f "$file" ]]; then # Good
if [ -f "$file" ]; then # POSIX only
Parameter Expansion
# 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
# Examples
file="document.txt"
echo "${file%%.*}" # "document" (remove extension)
echo "${file##*.}" # "txt" (get extension)
Functions
# REQUIRED: Use local variables
get_user_name() {
local user_id=$1
local name
name=$(grep "^${user_id}:" /etc/passwd | cut -d: -f5)
echo "$name"
}
# Return values via stdout
result=$(get_user_name "1000")
# Return status codes
validate_file() {
local file=$1
if [[ ! -f "$file" ]]; then
echo "Error: File not found: $file" >&2
return 1
fi
return 0
}
if validate_file "$input_file"; then
process_file "$input_file"
fi
Arrays
# Indexed arrays
files=("file1.txt" "file2.txt" "file3.txt")
echo "${files[0]}" # First element
echo "${files[@]}" # All elements
echo "${#files[@]}" # Array length
# Iterate safely
for file in "${files[@]}"; do
echo "Processing: $file"
done
# Associative arrays (Bash 4+)
declare -A config
config[host]="localhost"
config[port]="8080"
echo "${config[host]}:${config[port]}"
File Operations
# Read file line by line
while IFS= read -r line; do
echo "Line: $line"
done output.txt &2
}
trap 'error_handler $LINENO' ERR
# Check command exists
if ! command -v python3 &>/dev/null; then
echo "Error: python3 not found" >&2
exit 1
fi
# Conditional execution
command1 && command2 # Run command2 only if command1 succeeds
command1 || command2 # Run command2 only if command1 fails
Argument Parsing with getopts
usage() {
echo "Usage: $0 [-v] [-o output] [-h]"
echo " -v Verbose mode"
echo " -o FILE Output file"
echo " -h Show help"
exit 1
}
verbose=false
output_file=""
while getopts "vo:h" opt; do
case $opt in
v) verbose=true ;;
o) output_file="$OPTARG" ;;
h) usage ;;
*) usage ;;
esac
done
shift $((OPTIND - 1))
# Remaining args in $@
Logging
log() {
local level=$1
shift
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [$level] $*" >&2
}
log_info() { log "INFO" "$@"; }
log_warn() { log "WARN" "$@"; }
log_error() { log "ERROR" "$@"; }
# Usage
log_info "Starting process"
log_error "Failed to connect"
Debugging
# Enable debugging
set -x # Print commands
PS4='+ ${BASH_SOURCE}:${LINENO}: ' # Better debug output
# Debug specific section
set -x
# code to debug
set +x
# Run script with debug
bash -x script.sh
bash -n script.sh # Syntax check only
Common Patterns
# Check if root
if [[ $EUID -ne 0 ]]; then
echo "This script must be run as root" >&2
exit 1
fi
# Safe directory change
cd "$target_dir" || exit 1
# Process files safely (handles spaces)
find . -name "*.txt" -print0 | while IFS= read -r -d '' file; do
echo "Processing: $file"
done
# Retry pattern
retry() {
local max_attempts=$1
local delay=$2
shift 2
local attempt=1
while [[ $attempt -le $max_attempts ]]; do
if "$@"; then
return 0
fi
log_warn "Attempt $attempt failed, retrying in ${delay}s..."
sleep "$delay"
((attempt++))
done
return 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
# Run ShellCheck
shellcheck script.sh
# Disable specific warning (sparingly)
# shellcheck disable=SC2086
echo $UNQUOTED_VAR
# Follow sourced files
shellcheck -x script.sh
Testing with bats-core
#!/usr/bin/env bats
# File: test_script.bats
source ./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:
bats tests/
POSIX Compatibility
For maximum portability (sh, dash, ash):
#!/bin/sh
# Use [ ] instead of [[ ]]
if [ -f "file.txt" ]; then
echo "File exists"
fi
# No arrays, use positional parameters
set -- "apple" "banana" "cherry"
echo "First: $1"
# No $() in older shells, use backticks
current_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
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: b33eep
- Source: b33eep/claude-code-setup
- License: MIT
- Homepage: https://b33eep.github.io/claude-code-setup/
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.