Skip to content

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”

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 | |
| +-------------------------------------------------------------+ |
| |
+------------------------------------------------------------------+
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 |
| |
+------------------------------------------------------------------+

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 | |
| +----------------------------------------------------------+ |
| |
+------------------------------------------------------------------+
#!/bin/bash
# =============================================================================
# BASIC VARIABLES
# =============================================================================
# String variables
name="John"
age=30
# Using variables
echo "Name: $name"
echo "Age: ${age}"
# Read-only variables
readonly MAX_CONNECTIONS=100
# MAX_CONNECTIONS=200 # This will error
# Unset variable
unset name
echo "Name after unset: $name" # Empty
# =============================================================================
# STRING OPERATIONS
# =============================================================================
text="Hello World"
# Length
echo ${#text} # 11
# Substring
echo ${text:0:5} # Hello
echo ${text:6} # World
# Replace
echo ${text/World/Linux} # Hello Linux
echo ${text//l/L} # HeLLo WorLd
# Pattern removal
filename="/path/to/file.txt"
echo ${filename##*/} # file.txt (remove longest match)
echo ${filename#*/} # path/to/file.txt
# Case conversion
echo ${text^^} # HELLO WORLD
echo ${text,,} # hello world
# =============================================================================
# ARITHMETIC
# =============================================================================
a=10
b=3
# Using let
let sum=a+b
let product=a*b
# Using $(( ))
result=$((a + b))
result=$((a - b))
result=$((a * b))
result=$((a / b)) # Integer division
result=$((a % b)) # Modulo
# Using expr (older)
result=$(expr $a + $b)
# Increment/decrement
((a++))
((++a))
((a--))
# =============================================================================
# ARRAYS
# =============================================================================
# Define array
fruits=("apple" "banana" "cherry")
# Access elements
echo ${fruits[0]} # apple
echo ${fruits[@]} # All elements
echo ${!fruits[@]} # Indices
echo ${#fruits[@]} # Length
# Slicing
echo ${fruits[@]:1:2} # banana cherry
# Adding elements
fruits+=(date elderberry)
# Associative arrays (bash 4+)
declare -A user_info
user_info[name]="John"
user_info[email]="john@example.com"
echo ${user_info[name]}
# =============================================================================
# SPECIAL PARAMETERS
# =============================================================================
# $0 - Script name
echo "Script: $0"
# $1-$9 - Positional parameters
echo "First: $1, Second: $2"
# $# - Number of arguments
echo "Arguments: $#"
# $@ - All arguments as array
for arg in "$@"; do
echo "Arg: $arg"
done
# $* - All arguments as single string
for arg in "$*"; do
echo "All: $arg"
done
# $? - Exit status of last command
ls /nonexistent
echo "Exit: $?"
# $$ - Current PID
echo "PID: $$"
# $! - PID of last background job
tail -f /var/log/syslog &
echo "Background PID: $!"

#!/bin/bash
# =============================================================================
# IF/ELSE STATEMENTS
# =============================================================================
# Basic if
if [ $a -eq $b ]; then
echo "Equal"
fi
# If-else
if [ $a -gt $b ]; then
echo "A is greater"
else
echo "B is greater or equal"
fi
# Multiple conditions
if [ $a -gt 10 ] && [ $b -lt 5 ]; then
echo "Both conditions true"
fi
# Elif
if [ $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 tests
if [ -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 tests
if [ -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 tests
if [ $a -eq $b ]; then # equal
if [ $a -ne $b ]; then # not equal
if [ $a -gt $b ]; then # greater than
if [ $a -ge $b ]; then # greater or equal
if [ $a -lt $b ]; then # less than
if [ $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 case
case $filename in
*.txt)
echo "Text file"
;;
*.jpg|*.png)
echo "Image file"
;;
*.log)
echo "Log file"
;;
*)
echo "Unknown file type"
;;
esac
#!/bin/bash
# =============================================================================
# FOR LOOPS
# =============================================================================
# Simple for loop
for i in 1 2 3 4 5; do
echo "Number: $i"
done
# For loop with range
for i in {1..10}; do
echo $i
done
# For loop with step
for i in {0..100..10}; do
echo $i
done
# For loop over files
for file in *.txt; do
echo "Processing: $file"
done
# For loop over directories
for dir in /home/*/; do
echo "Directory: $dir"
done
# C-style for loop
for ((i=0; i<10; i++)); do
echo $i
done
# For loop with command output
for user in $(cut -d: -f1 /etc/passwd); do
echo "User: $user"
done
# For loop with arrays
colors=("red" "green" "blue")
for color in "${colors[@]}"; do
echo "Color: $color"
done
# =============================================================================
# WHILE LOOPS
# =============================================================================
# Basic while
count=1
while [ $count -le 5 ]; do
echo "Count: $count"
((count++))
done
# While reading file
while read line; do
echo "Line: $line"
done < /etc/hosts
# While with multiple conditions
while [ $a -gt 0 ] && [ $b -lt 100 ]; do
((a--))
((b++))
done
# Infinite while loop
while true; do
echo "Running..."
sleep 60
done
# =============================================================================
# UNTIL LOOPS
# =============================================================================
# Until - executes until condition is true
count=1
until [ $count -gt 5 ]; do
echo "Until: $count"
((count++))
done
# =============================================================================
# LOOP CONTROL
# =============================================================================
# Break
for i in {1..10}; do
if [ $i -eq 5 ]; then
break
fi
echo $i
done
# Continue
for i in {1..10}; do
if [ $i -eq 5 ]; then
continue
fi
echo $i
done
# While with break on condition
while true; do
read -p "Enter 'quit' to exit: " input
if [ "$input" = "quit" ]; then
break
fi
done

#!/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 function
greet "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 value
get_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 arguments
print_all() {
for arg in "$@"; do
echo "$arg"
done
}
print_all one two three four
# =============================================================================
# FUNCTION SCOPE
# =============================================================================
# Global variable
global_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 arguments
greet() {
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)"

#!/bin/bash
# =============================================================================
# BASIC INPUT
# =============================================================================
# Simple read
echo "Enter your name:"
read name
echo "Hello, $name"
# Read with prompt
read -p "Enter your name: " name
# Silent read (password)
read -sp "Enter password: " password
echo
# Read with timeout
read -t 5 -p "Enter value: " value
if [ $? -eq 0 ]; then
echo "Value: $value"
else
echo "Timed out"
fi
# Read limited characters
read -n 10 -p "Enter 10 chars: " input
# Read into array
read -a words -p "Enter words: "
echo "${words[@]}"
# =============================================================================
# COMMAND LINE ARGUMENTS
# =============================================================================
# Simple arguments
echo "Script: $0"
echo "First: $1"
echo "Second: $2"
echo "All: $@"
echo "Count: $#"
# Shift arguments
while [ $# -gt 0 ]; do
echo "Arg: $1"
shift
done
# Getopts for option parsing
while getopts "hvu:" opt; do
case $opt in
h)
echo "Help"
;;
v)
echo "Version 1.0"
;;
u)
echo "User: $OPTARG"
;;
\?)
echo "Invalid option"
;;
esac
done
# =============================================================================
# OUTPUT REDIRECTION
# =============================================================================
# Redirect to file (overwrite)
echo "Hello" > output.txt
# Append to file
echo "World" >> output.txt
# Redirect stderr
command 2> error.log
# Redirect both stdout and stderr
command &> output.log
# Redirect stdout to stderr
echo "Error message" >&2
# Here documents
cat << EOF
This is a multi-line
text that will be
displayed or written to a file
EOF
# Here strings
cat <<< "Single line string"
# =============================================================================
# FILE DESCRIPTORS
# =============================================================================
# Open file descriptor
exec 3> output.txt
# Write to descriptor
echo "Line 1" >&3
echo "Line 2" >&3
# Close descriptor
exec 3>&-
# Read from descriptor
exec 3< input.txt
read line <&3
exec 3<&-

#!/bin/bash
# =============================================================================
# EXIT STATUS
# =============================================================================
# Check exit status
command
if [ $? -eq 0 ]; then
echo "Success"
fi
# Combine with command
if command; then
echo "Success"
fi
# Exit with status
exit 0 # Success
exit 1 # General error
exit 2 # Misuse of command
exit 126 # Command not executable
exit 127 # Command not found
exit 128 # Invalid exit argument
# =============================================================================
# SET OPTIONS
# =============================================================================
# Exit on error
set -e
set -o errexit
# Exit on undefined variable
set -u
set -o nounset
# Exit on pipe failure
set -o pipefail
# Combine for strict mode
set -euo pipefail
# =============================================================================
# TRAP ERRORS
# =============================================================================
# Trap on EXIT
cleanup() {
echo "Cleaning up..."
# Remove temp files
rm -f /tmp/temp_$$
}
trap cleanup EXIT
# Trap on ERROR
error_handler() {
echo "Error on line $1"
}
trap 'error_handler $LINENO' ERR
# Trap on DEBUG
debug_handler() {
echo "Executing: $BASH_COMMAND"
}
trap debug_handler DEBUG
# Trap on RETURN
return_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 1
fi

script-name.sh
#!/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 << EOF
Usage: $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 "$@"

Important

  1. Shebang: Use #!/usr/bin/env bash for portability
  2. Variables: Always quote variables: "$var" not $var
  3. Arithmetic: Use $(( )) for arithmetic
  4. Arrays: Use ("${array[@]}") to preserve elements with spaces
  5. Exit Status: Check with $? or if cmd; then
  6. Strict Mode: Use set -euo pipefail
  7. Functions: Use local for function variables
  8. Error Handling: Use traps for cleanup
  9. Getopts: Use for option parsing
  10. Best Practice: Create reusable script template

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

Chapter 47: Advanced Bash Scripting


Last Updated: February 2026