Bash_fundamentals
Chapter 46: Bash Scripting Fundamentals - Deep Dive
Section titled “Chapter 46: Bash Scripting Fundamentals - Deep Dive”Mastering Bash Scripting for System Administration
Section titled “Mastering Bash Scripting for System Administration”46.1 Understanding Bash
Section titled “46.1 Understanding Bash”What is Bash?
Section titled “What is Bash?”Bash (Bourne Again SHell) is the default shell for most Linux distributions. It’s both a command interpreter and a scripting language, making it essential for system administration automation.
Bash Position in Linux+------------------------------------------------------------------+| || User Space || +-------------------------------------------------------------+ || | Applications | || +-------------------------------------------------------------+ || | || v || +-------------------------------------------------------------+ || | Shells | || | +----------+ +----------+ +----------+ +----------+ | || | | bash | | zsh | | fish | | sh | | || | +----------+ +----------+ +----------+ +----------+ | || | | | || | Bash is the DEFAULT | || +-------------------------------------------------------------+ || | || v || +-------------------------------------------------------------+ || | System Calls (syscalls) | || +-------------------------------------------------------------+ || | || v || +-------------------------------------------------------------+ || | Kernel Space | || +-------------------------------------------------------------+ || |+------------------------------------------------------------------+Script Execution Flow
Section titled “Script Execution Flow” Bash Script Execution Flow+------------------------------------------------------------------+| || 1. Read script file || | || v || 2. Parse shebang || +----------------------------------------------------------+ || | #!/bin/bash - Use /bin/bash interpreter | || | #!/usr/bin/env bash - Find bash in PATH | || +----------------------------------------------------------+ || | || v || 3. Expand variables || +----------------------------------------------------------+ || | $VAR, ${VAR}, $1, $@, $$ | || +----------------------------------------------------------+ || | || v || 4. Parse commands || +----------------------------------------------------------+ || | Built-ins: cd, echo, export, set | || | External: grep, sed, awk, cat, ls | || +----------------------------------------------------------+ || | || v || 5. Execute commands || | || v || 6. Return exit status || |+------------------------------------------------------------------+46.2 Variables and Data Types
Section titled “46.2 Variables and Data Types”Variable Types
Section titled “Variable Types” Variable Types in Bash+------------------------------------------------------------------+| || String Variables || +----------------------------------------------------------+ || | name="John" | || | message='Hello World' | || | greeting="Welcome, ${name}!" | || +----------------------------------------------------------+ || || Integer Variables || +----------------------------------------------------------+ || | declare -i count=10 | || | let sum=5+3 | || | ((total = 100 + 50)) | || +----------------------------------------------------------+ || || Array Variables || +----------------------------------------------------------+ || | fruits=(apple banana cherry) | || | declare -a servers=("web1" "web2") | || +----------------------------------------------------------+ || || Environment Variables || +----------------------------------------------------------+ || | export DB_HOST="localhost" | || | export -p | || +----------------------------------------------------------+ || || Special Variables || +----------------------------------------------------------+ || | $0 - Script name, $1-$9 - Arguments | || | $# - Arg count, $@ - All args | || | $? - Exit status, $$ - PID | || +----------------------------------------------------------+ || |+------------------------------------------------------------------+Variable Operations
Section titled “Variable Operations”#!/bin/bash
# =============================================================================# BASIC VARIABLES# =============================================================================
# String variablesname="John"age=30
# Using variablesecho "Name: $name"echo "Age: ${age}"
# Read-only variablesreadonly MAX_CONNECTIONS=100# MAX_CONNECTIONS=200 # This will error
# Unset variableunset nameecho "Name after unset: $name" # Empty
# =============================================================================# STRING OPERATIONS# =============================================================================
text="Hello World"
# Lengthecho ${#text} # 11
# Substringecho ${text:0:5} # Helloecho ${text:6} # World
# Replaceecho ${text/World/Linux} # Hello Linuxecho ${text//l/L} # HeLLo WorLd
# Pattern removalfilename="/path/to/file.txt"echo ${filename##*/} # file.txt (remove longest match)echo ${filename#*/} # path/to/file.txt
# Case conversionecho ${text^^} # HELLO WORLDecho ${text,,} # hello world
# =============================================================================# ARITHMETIC# =============================================================================
a=10b=3
# Using letlet sum=a+blet product=a*b
# Using $(( ))result=$((a + b))result=$((a - b))result=$((a * b))result=$((a / b)) # Integer divisionresult=$((a % b)) # Modulo
# Using expr (older)result=$(expr $a + $b)
# Increment/decrement((a++))((++a))((a--))
# =============================================================================# ARRAYS# =============================================================================
# Define arrayfruits=("apple" "banana" "cherry")
# Access elementsecho ${fruits[0]} # appleecho ${fruits[@]} # All elementsecho ${!fruits[@]} # Indicesecho ${#fruits[@]} # Length
# Slicingecho ${fruits[@]:1:2} # banana cherry
# Adding elementsfruits+=(date elderberry)
# Associative arrays (bash 4+)declare -A user_infouser_info[name]="John"user_info[email]="john@example.com"echo ${user_info[name]}
# =============================================================================# SPECIAL PARAMETERS# =============================================================================
# $0 - Script nameecho "Script: $0"
# $1-$9 - Positional parametersecho "First: $1, Second: $2"
# $# - Number of argumentsecho "Arguments: $#"
# $@ - All arguments as arrayfor arg in "$@"; do echo "Arg: $arg"done
# $* - All arguments as single stringfor arg in "$*"; do echo "All: $arg"done
# $? - Exit status of last commandls /nonexistentecho "Exit: $?"
# $$ - Current PIDecho "PID: $$"
# $! - PID of last background jobtail -f /var/log/syslog &echo "Background PID: $!"46.3 Control Structures
Section titled “46.3 Control Structures”Conditional Statements
Section titled “Conditional Statements”#!/bin/bash
# =============================================================================# IF/ELSE STATEMENTS# =============================================================================
# Basic ifif [ $a -eq $b ]; then echo "Equal"fi
# If-elseif [ $a -gt $b ]; then echo "A is greater"else echo "B is greater or equal"fi
# Multiple conditionsif [ $a -gt 10 ] && [ $b -lt 5 ]; then echo "Both conditions true"fi
# Elifif [ $score -ge 90 ]; then echo "A"elif [ $score -ge 80 ]; then echo "B"elif [ $score -ge 70 ]; then echo "C"else echo "F"fi
# =============================================================================# TEST OPERATORS# =============================================================================
# File testsif [ -f "/path/to/file" ]; then echo "File exists and is regular"fi
if [ -d "/path/to/dir" ]; then echo "Directory exists"fi
if [ -r "/path/to/file" ]; then echo "File is readable"fi
if [ -w "/path/to/file" ]; then echo "File is writable"fi
if [ -x "/path/to/file" ]; then echo "File is executable"fi
if [ -s "/path/to/file" ]; then echo "File exists and is not empty"fi
# String testsif [ -z "$string" ]; then echo "String is empty"fi
if [ -n "$string" ]; then echo "String is not empty"fi
if [ "$str1" = "$str2" ]; then echo "Strings equal"fi
if [ "$str1" != "$str2" ]; then echo "Strings not equal"fi
# Numeric testsif [ $a -eq $b ]; then # equalif [ $a -ne $b ]; then # not equalif [ $a -gt $b ]; then # greater thanif [ $a -ge $b ]; then # greater or equalif [ $a -lt $b ]; then # less thanif [ $a -le $b ]; then # less or equal
# =============================================================================# CASE STATEMENT# =============================================================================
case $option in start) echo "Starting service..." ;; stop) echo "Stopping service..." ;; restart) echo "Restarting service..." ;; status) echo "Service status" ;; *) echo "Unknown option: $option" exit 1 ;;esac
# Pattern matching in casecase $filename in *.txt) echo "Text file" ;; *.jpg|*.png) echo "Image file" ;; *.log) echo "Log file" ;; *) echo "Unknown file type" ;;esacLoop Structures
Section titled “Loop Structures”#!/bin/bash
# =============================================================================# FOR LOOPS# =============================================================================
# Simple for loopfor i in 1 2 3 4 5; do echo "Number: $i"done
# For loop with rangefor i in {1..10}; do echo $idone
# For loop with stepfor i in {0..100..10}; do echo $idone
# For loop over filesfor file in *.txt; do echo "Processing: $file"done
# For loop over directoriesfor dir in /home/*/; do echo "Directory: $dir"done
# C-style for loopfor ((i=0; i<10; i++)); do echo $idone
# For loop with command outputfor user in $(cut -d: -f1 /etc/passwd); do echo "User: $user"done
# For loop with arrayscolors=("red" "green" "blue")for color in "${colors[@]}"; do echo "Color: $color"done
# =============================================================================# WHILE LOOPS# =============================================================================
# Basic whilecount=1while [ $count -le 5 ]; do echo "Count: $count" ((count++))done
# While reading filewhile read line; do echo "Line: $line"done < /etc/hosts
# While with multiple conditionswhile [ $a -gt 0 ] && [ $b -lt 100 ]; do ((a--)) ((b++))done
# Infinite while loopwhile true; do echo "Running..." sleep 60done
# =============================================================================# UNTIL LOOPS# =============================================================================
# Until - executes until condition is truecount=1until [ $count -gt 5 ]; do echo "Until: $count" ((count++))done
# =============================================================================# LOOP CONTROL# =============================================================================
# Breakfor i in {1..10}; do if [ $i -eq 5 ]; then break fi echo $idone
# Continuefor i in {1..10}; do if [ $i -eq 5 ]; then continue fi echo $idone
# While with break on conditionwhile true; do read -p "Enter 'quit' to exit: " input if [ "$input" = "quit" ]; then break fidone46.4 Functions
Section titled “46.4 Functions”Function Definitions
Section titled “Function Definitions”#!/bin/bash
# =============================================================================# FUNCTION BASICS# =============================================================================
# Function definition (style 1)function greet { echo "Hello, $1!"}
# Function definition (style 2)greet_user() { echo "Hello, $1!" return 0}
# Call functiongreet "World"
# =============================================================================# FUNCTION WITH RETURN VALUE# =============================================================================
# Return status (0-255)check_service() { if systemctl is-active --quiet nginx; then return 0 # Success else return 1 # Failure fi}
if check_service; then echo "Nginx is running"fi
# Return string valueget_date() { echo $(date +%Y-%m-%d)}
today=$(get_date)echo "Today: $today"
# =============================================================================# FUNCTION WITH ARGUMENTS# =============================================================================
sum() { local a=$1 local b=$2 echo $((a + b))}
result=$(sum 10 20)echo "Sum: $result"
# Variable argumentsprint_all() { for arg in "$@"; do echo "$arg" done}
print_all one two three four
# =============================================================================# FUNCTION SCOPE# =============================================================================
# Global variableglobal_var="I'm global"
test_scope() { # Local variable local local_var="I'm local"
# Modify global global_var="Modified global"
echo "Inside function:" echo " Global: $global_var" echo " Local: $local_var"}
test_scope
echo "Outside function:"echo " Global: $global_var"# echo " Local: $local_var" # This would error
# =============================================================================# FUNCTION OVERLOADING (SIMULATED)# =============================================================================
# Use default argumentsgreet() { local name=${1:-"User"} local greeting=${2:-"Hello"} echo "$greeting, $name!"}
greet "John" # Hello, John!greet "Jane" "Hi" # Hi, Jane!greet # Hello, User!
# =============================================================================# RECURSIVE FUNCTIONS# =============================================================================
factorial() { local n=$1 if [ $n -le 1 ]; then echo 1 else local prev=$(factorial $((n-1))) echo $((n * prev)) fi}
echo "Factorial of 5: $(factorial 5)"46.5 Input/Output
Section titled “46.5 Input/Output”Reading Input
Section titled “Reading Input”#!/bin/bash
# =============================================================================# BASIC INPUT# =============================================================================
# Simple readecho "Enter your name:"read nameecho "Hello, $name"
# Read with promptread -p "Enter your name: " name
# Silent read (password)read -sp "Enter password: " passwordecho
# Read with timeoutread -t 5 -p "Enter value: " valueif [ $? -eq 0 ]; then echo "Value: $value"else echo "Timed out"fi
# Read limited charactersread -n 10 -p "Enter 10 chars: " input
# Read into arrayread -a words -p "Enter words: "echo "${words[@]}"
# =============================================================================# COMMAND LINE ARGUMENTS# =============================================================================
# Simple argumentsecho "Script: $0"echo "First: $1"echo "Second: $2"echo "All: $@"echo "Count: $#"
# Shift argumentswhile [ $# -gt 0 ]; do echo "Arg: $1" shiftdone
# Getopts for option parsingwhile getopts "hvu:" opt; do case $opt in h) echo "Help" ;; v) echo "Version 1.0" ;; u) echo "User: $OPTARG" ;; \?) echo "Invalid option" ;; esacdone
# =============================================================================# OUTPUT REDIRECTION# =============================================================================
# Redirect to file (overwrite)echo "Hello" > output.txt
# Append to fileecho "World" >> output.txt
# Redirect stderrcommand 2> error.log
# Redirect both stdout and stderrcommand &> output.log
# Redirect stdout to stderrecho "Error message" >&2
# Here documentscat << EOFThis is a multi-linetext that will bedisplayed or written to a fileEOF
# Here stringscat <<< "Single line string"
# =============================================================================# FILE DESCRIPTORS# =============================================================================
# Open file descriptorexec 3> output.txt
# Write to descriptorecho "Line 1" >&3echo "Line 2" >&3
# Close descriptorexec 3>&-
# Read from descriptorexec 3< input.txtread line <&3exec 3<&-46.6 Error Handling
Section titled “46.6 Error Handling”Exit Status and Error Handling
Section titled “Exit Status and Error Handling”#!/bin/bash
# =============================================================================# EXIT STATUS# =============================================================================
# Check exit statuscommandif [ $? -eq 0 ]; then echo "Success"fi
# Combine with commandif command; then echo "Success"fi
# Exit with statusexit 0 # Successexit 1 # General errorexit 2 # Misuse of commandexit 126 # Command not executableexit 127 # Command not foundexit 128 # Invalid exit argument
# =============================================================================# SET OPTIONS# =============================================================================
# Exit on errorset -eset -o errexit
# Exit on undefined variableset -uset -o nounset
# Exit on pipe failureset -o pipefail
# Combine for strict modeset -euo pipefail
# =============================================================================# TRAP ERRORS# =============================================================================
# Trap on EXITcleanup() { echo "Cleaning up..." # Remove temp files rm -f /tmp/temp_$$}
trap cleanup EXIT
# Trap on ERRORerror_handler() { echo "Error on line $1"}trap 'error_handler $LINENO' ERR
# Trap on DEBUGdebug_handler() { echo "Executing: $BASH_COMMAND"}trap debug_handler DEBUG
# Trap on RETURNreturn_handler() { echo "Function returned"}trap return_handler RETURN
# =============================================================================# ERROR HANDLING IN FUNCTIONS# =============================================================================
safe_command() { set +e # Don't exit on error command_that_might_fail local status=$? set -e # Re-enable
if [ $status -ne 0 ]; then echo "Command failed with status $status" return $status fi return 0}
# =============================================================================# VALIDATION# =============================================================================
validate_input() { if [ -z "$1" ]; then echo "Error: Empty argument" >&2 return 1 fi
if [ ! -f "$1" ]; then echo "Error: File not found: $1" >&2 return 2 fi
return 0}
if ! validate_input "$config_file"; then exit 1fi46.7 Best Practices
Section titled “46.7 Best Practices”Script Template
Section titled “Script Template”#!/usr/bin/env bash
# =============================================================================# DESCRIPTION: What this script does# AUTHOR: Your Name# VERSION: 1.0.0# =============================================================================
set -euo pipefail # Strict mode
# =============================================================================# CONSTANTS# =============================================================================
readonly SCRIPT_NAME=$(basename "$0")readonly SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)readonly LOG_FILE="/var/log/${SCRIPT_NAME}.log"
# =============================================================================# FUNCTIONS# =============================================================================
usage() { cat << EOFUsage: $SCRIPT_NAME [OPTIONS]
OPTIONS: -h, --help Show this help message -v, --verbose Enable verbose output -f, --file Input file (required) -n, --dry-run Dry run mode
EXAMPLES: $SCRIPT_NAME -f input.txt $SCRIPT_NAME --dry-run --verbose
EOF exit 1}
log() { local level=$1 shift echo "[$(date +'%Y-%m-%d %H:%M:%S')] [$level] $*" | tee -a "$LOG_FILE"}
error() { log "ERROR" "$@" >&2}
info() { log "INFO" "$@"}
# =============================================================================# MAIN# =============================================================================
main() { local verbose=false local dry_run=false local input_file=""
# Parse arguments while [[ $# -gt 0 ]]; do case $1 in -h|--help) usage ;; -v|--verbose) verbose=true shift ;; -n|--dry-run) dry_run=true shift ;; -f|--file) input_file="$2" shift 2 ;; *) error "Unknown option: $1" usage ;; esac done
# Validate arguments if [ -z "$input_file" ]; then error "Input file is required" usage fi
if [ ! -f "$input_file" ]; then error "Input file not found: $input_file" exit 1 fi
# Main logic info "Starting $SCRIPT_NAME"
if [ "$dry_run" = true ]; then info "Dry run mode - no changes will be made" fi
# Your code here
info "Completed $SCRIPT_NAME"}
# =============================================================================# ENTRY POINT# =============================================================================
main "$@"46.8 Exam Tips
Section titled “46.8 Exam Tips”- Shebang: Use
#!/usr/bin/env bashfor portability - Variables: Always quote variables:
"$var"not$var - Arithmetic: Use
$(( ))for arithmetic - Arrays: Use
("${array[@]}")to preserve elements with spaces - Exit Status: Check with
$?orif cmd; then - Strict Mode: Use
set -euo pipefail - Functions: Use
localfor function variables - Error Handling: Use traps for cleanup
- Getopts: Use for option parsing
- Best Practice: Create reusable script template
Summary
Section titled “Summary”In this chapter, you learned:
- ✅ Bash scripting fundamentals
- ✅ Variables, strings, and arrays
- ✅ Arithmetic operations
- ✅ Conditional statements (if, case)
- ✅ Loop structures (for, while, until)
- ✅ Functions and scope
- ✅ Input/output operations
- ✅ Error handling and best practices
- ✅ Script templates and patterns
Next Chapter
Section titled “Next Chapter”Chapter 47: Advanced Bash Scripting
Last Updated: February 2026