Best_practices
Chapter 33: Bash Scripting Best Practices
Section titled “Chapter 33: Bash Scripting Best Practices”Overview
Section titled “Overview”This chapter covers the best practices for writing production-quality bash scripts. Following these practices ensures your scripts are reliable, maintainable, and secure.
Script Structure
Section titled “Script Structure”Recommended Template
Section titled “Recommended Template”#!/usr/bin/env bash## Script description## Usage: script.sh [OPTIONS]## Options:# -h, --help Show this help message# -v, --verbose Enable verbose output# -f, --file Input file## Author: Your Name# Version: 1.0.0#
set -euo pipefail
# Global variablesreadonly SCRIPT_NAME="$(basename "$0")"readonly SCRIPT_VERSION="1.0.0"
# Usage functionusage() { sed -n '3,15p' "$0"}
# Logginglog() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"; }error() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] ERROR: $*" >&2; }
# Cleanupcleanup() { # Add cleanup code true}trap cleanup EXIT
# Main functionmain() { # Parse arguments while [[ $# -gt 0 ]]; do case "$1" in -h|--help) usage exit 0 ;; -v|--verbose) VERBOSE=1 shift ;; -f|--file) FILE="$2" shift 2 ;; *) error "Unknown option: $1" usage exit 1 ;; esac done
# Script logic log "Starting $SCRIPT_NAME"}
main "$@"Variable Best Practices
Section titled “Variable Best Practices”Use readonly for Constants
Section titled “Use readonly for Constants”# Goodreadonly MAX_RETRIES=3readonly CONFIG_FILE="/etc/app.conf"
# AvoidMAX_RETRIES=3 # Can be modifiedUse Uppercase for Constants
Section titled “Use Uppercase for Constants”# Constantsreadonly MAX_CONNECTIONS=100readonly DEFAULT_PORT=8080
# Variables (lowercase)local count=0local filename=""Always Quote Variables
Section titled “Always Quote Variables”# Goodecho "Hello $name"rm -rf "$directory"
# Avoidecho "Hello $name" # Unquotedrm -rf $directory # Dangerous!Function Best Practices
Section titled “Function Best Practices”Use local Variables
Section titled “Use local Variables”# Goodmy_function() { local result="" local -i count=0
# function code}
# Avoid global variables in functionsmy_function() { result="" # Creates global variable}Use return for Exit Status
Section titled “Use return for Exit Status”# Good - return exit statuscheck_file() { if [[ -f "$1" ]]; then return 0 else return 1 fi}
# Not for returning data# Use echo or assign to variableError Handling Best Practices
Section titled “Error Handling Best Practices”Always Use set -euo pipefail
Section titled “Always Use set -euo pipefail”#!/usr/bin/env bashset -euo pipefailCheck Command Exit Status
Section titled “Check Command Exit Status”# Goodif ! command; then echo "Command failed" exit 1fi
# Or using &&command && echo "Success"
# Or using ||command || { echo "Failed"; exit 1; }Use Trap for Cleanup
Section titled “Use Trap for Cleanup”#!/bin/bash
cleanup() { # Remove temp files # Close file descriptors # Kill child processes}
trap cleanup EXITtrap 'error "Interrupted"; exit 130' INT TERMInput Validation
Section titled “Input Validation”Validate Required Arguments
Section titled “Validate Required Arguments”#!/bin/bash
if [[ $# -lt 2 ]]; then echo "Usage: $0 <arg1> <arg2>" exit 1fiValidate Environment Variables
Section titled “Validate Environment Variables”: "${DATABASE_URL:?Environment variable DATABASE_URL is required}": "${API_KEY:?Environment variable API_KEY is required}"Validate Input Values
Section titled “Validate Input Values”if [[ ! "$port" =~ ^[0-9]+$ ]]; then echo "Error: Port must be a number" exit 1fi
if [[ "$port" -lt 1 ]] || [[ "$port" -gt 65535 ]]; then echo "Error: Port must be between 1 and 65535" exit 1fiSecurity Best Practices
Section titled “Security Best Practices”Never Hardcode Secrets
Section titled “Never Hardcode Secrets”# BadPASSWORD="secret123"
# Good - use environment variables: "${DB_PASSWORD:?DB_PASSWORD must be set}"Use — for Argument Separation
Section titled “Use — for Argument Separation”# Goodrm -- "$file"command -- "$arg"
# Avoid ambiguous argumentsrm -f "$file" # Could be confused with filenameValidate Filenames
Section titled “Validate Filenames”# Prevent path traversalif [[ "$filename" == *".."* ]]; then echo "Invalid filename" exit 1fi
# Use absolute paths when neededfilepath="$(realpath "$filepath")"Performance Best Practices
Section titled “Performance Best Practices”Avoid Subshells in Loops
Section titled “Avoid Subshells in Loops”# Bad - subshell in loopfor item in items; do result=$(process "$item")done
# Better - process in loopfor item in items; do process "$item"doneUse Built-ins When Possible
Section titled “Use Built-ins When Possible”# Good - built-in[[ $var == "value" ]]
# Slower - external commandtest "$var" = "value"
# Good - built-in string operationsecho "${var##*/}" # Basenameecho "${var%/*}" # DirnameUse Arrays for Multiple Items
Section titled “Use Arrays for Multiple Items”# Goodfiles=( *.txt )for file in "${files[@]}"; do process "$file"done
# Avoid parsing command output in loopsfor file in $(ls *.txt); do # Word splitting issues! process "$file"doneDocumentation
Section titled “Documentation”Comment Your Code
Section titled “Comment Your Code”# Function description# Processes the input file and generates a reportprocess_file() { # Local variables local input_file="$1"
# Check if file exists if [[ ! -f "$input_file" ]]; then return 1 fi
# Process the file # ...}Use Descriptive Names
Section titled “Use Descriptive Names”# Goodreadonly MAX_RETRY_ATTEMPTS=3local backup_filename=""
# Avoidreadonly M=3local b=""Testing
Section titled “Testing”Test Error Conditions
Section titled “Test Error Conditions”# Test missing arguments./script.sh# Should show usage
# Test invalid input./script.sh -p invalid# Should show error
# Test with required env varsDATABASE_URL="" ./script.sh# Should failSummary
Section titled “Summary”In this chapter, you learned:
- ✅ Script structure and templates
- ✅ Variable best practices
- ✅ Function best practices
- ✅ Error handling best practices
- ✅ Input validation
- ✅ Security best practices
- ✅ Performance best practices
- ✅ Documentation
- ✅ Testing
Next Steps
Section titled “Next Steps”Continue to the next chapter to learn about Common One-liners.
Previous Chapter: Strict Mode Next Chapter: Common One-liners