Bash
Shell Scripting
Shell Scripting Script Basics #!/usr/bin/env bash # Shebang — use env to find bash in PATH # Best practice: set errexit, pipefail, nounset set -euo pipefail # -…
Shell Scripting
Script Basics
#!/usr/bin/env bash
# Shebang — use env to find bash in PATH
# Best practice: set errexit, pipefail, nounset
set -euo pipefail
# -e: exit on error
# -u: error on undefined variable
# -o pipefail: pipe fails if any command fails
# Variables
NAME="Alice"
AGE=28
GREETING="Hello, $NAME" # double quotes allow expansion
LITERAL='Hello, $NAME' # single quotes: literal string
# Read-only constants
readonly MAX_RETRIES=3
# Command substitution
TODAY=$(date +%Y-%m-%d)
FILES=$(ls *.sh 2>/dev/null | wc -l)
# Arithmetic
((count++))
((total = a + b))
result=$((10 * 5 - 3))
echo $((2 ** 8)) # 256Conditionals & Loops
# If / elif / else
if [[ -f "$FILE" ]]; then
echo "File exists"
elif [[ -d "$FILE" ]]; then
echo "Directory exists"
else
echo "Not found"
fi
# Test operators
# Files: -f (file), -d (dir), -e (exists), -r (readable), -x (executable)
# Strings: -z (empty), -n (non-empty), = (equal), != (not equal)
# Numbers: -eq, -ne, -lt, -le, -gt, -ge
# Logic: && (and), || (or), ! (not)
if [[ "$USER" = "root" ]] && [[ -x "/usr/local/bin/app" ]]; then
echo "root user with app"
fi
if [[ -z "$DB_URL" ]]; then
echo "Error: DB_URL not set" >&2
exit 1
fi
# Case
case "$ENV" in
production|prod)
PORT=443; DEBUG=false;;
staging|stage)
PORT=8443; DEBUG=true;;
*)
PORT=3000; DEBUG=true;;
esac
# For loops
for file in *.log; do
echo "Processing: $file"
gzip "$file"
done
for i in {1..5}; do echo "$i"; done
for ((i=0; i<10; i++)); do echo "$i"; done
for item in "${array[@]}"; do echo "$item"; done
# While loop
while IFS= read -r line; do
echo "Line: $line"
done < input.txt
# Until loop
until [[ -f "$LOCK_FILE" ]]; do
sleep 1
doneFunctions & Arrays
# Functions
log() {
local level="$1"
local message="$2"
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [$level] $message" >&2
}
retry() {
local max_attempts="$1"
local delay="$2"
shift 2
local command=("$@")
local attempt=1
while (( attempt <= max_attempts )); do
"${command[@]}" && return 0
log "WARN" "Attempt $attempt/$max_attempts failed. Retrying in ${delay}s..."
sleep "$delay"
((attempt++))
done
log "ERROR" "Command failed after $max_attempts attempts"
return 1
}
# Return values via echo (not return — that's for exit codes 0-255)
get_user() {
local id="$1"
echo "User_$id" # "return" data via echo
}
user=$(get_user 42)
# Arrays
files=("app.ts" "index.ts" "types.ts")
files+=("utils.ts") # append
echo "${files[0]}" # first element
echo "${files[@]}" # all elements
echo "${#files[@]}" # length
echo "${files[@]:1:2}" # slice [1,2]
unset files[1] # remove element
# Associative arrays (bash 4+)
declare -A config
config["host"]="localhost"
config["port"]="5432"
echo "${config[host]}"
for key in "${!config[@]}"; do echo "$key=${config[$key]}"; donePractical Patterns
#!/usr/bin/env bash
set -euo pipefail
# Script directory (works even when sourced)
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Cleanup on exit / error
cleanup() {
echo "Cleaning up..."
rm -f /tmp/tempfile.$$
}
trap cleanup EXIT
trap 'echo "Error on line $LINENO"; cleanup; exit 1' ERR
# Check required commands
require_cmd() {
command -v "$1" &>/dev/null || { echo "Error: $1 not found" >&2; exit 1; }
}
require_cmd docker
require_cmd kubectl
# Parse arguments
usage() {
cat <<EOF
Usage: $(basename "$0") [options]
-e, --env Environment (dev|staging|prod)
-v, --verbose Verbose output
-h, --help Show this help
EOF
exit 1
}
ENV="dev"
VERBOSE=false
while [[ $# -gt 0 ]]; do
case "$1" in
-e|--env) ENV="$2"; shift 2;;
-v|--verbose) VERBOSE=true; shift;;
-h|--help) usage;;
*) echo "Unknown option: $1" >&2; usage;;
esac
done
# Validate input
[[ "$ENV" =~ ^(dev|staging|prod)$ ]] || { echo "Invalid env: $ENV" >&2; exit 1; }
# Load .env file
if [[ -f ".env.$ENV" ]]; then
set -o allexport
source ".env.$ENV"
set +o allexport
fi