Control Structures
Conditionals
If Statements
# Basic if statement
if [ condition ]; then
echo "True"
fi
# If-else
if [ condition ]; then
echo "True"
else
echo "False"
fi
# If-elif-else
if [ condition1 ]; then
echo "First condition"
elif [ condition2 ]; then
echo "Second condition"
else
echo "Default"
fi
# Compact form
[ condition ] && echo "True" || echo "False"
Test Conditions
# File tests
if [ -f "$file" ]; then echo "File exists"; fi
if [ -d "$dir" ]; then echo "Directory exists"; fi
if [ -r "$file" ]; then echo "File is readable"; fi
if [ -w "$file" ]; then echo "File is writable"; fi
if [ -x "$file" ]; then echo "File is executable"; fi
if [ -s "$file" ]; then echo "File 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 are equal"; fi
if [ "$str1" != "$str2" ]; then echo "Strings are different"; fi
# Numeric tests
if [ "$num1" -eq "$num2" ]; then echo "Equal"; fi
if [ "$num1" -ne "$num2" ]; then echo "Not equal"; fi
if [ "$num1" -lt "$num2" ]; then echo "Less than"; fi
if [ "$num1" -le "$num2" ]; then echo "Less or equal"; fi
if [ "$num1" -gt "$num2" ]; then echo "Greater than"; fi
if [ "$num1" -ge "$num2" ]; then echo "Greater or equal"; fi
Advanced Conditionals
# Double brackets (bash extended test)
if [[ $string =~ ^[0-9]+$ ]]; then
echo "String is numeric"
fi
if [[ $string == *pattern* ]]; then
echo "String contains pattern"
fi
# Logical operators
if [[ condition1 && condition2 ]]; then
echo "Both conditions are true"
fi
if [[ condition1 || condition2 ]]; then
echo "At least one condition is true"
fi
if [[ ! condition ]]; then
echo "Condition is false"
fi
# Arithmetic conditions
if (( num > 0 && num < 100 )); then
echo "Number is between 0 and 100"
fi
Case Statements
Basic Case Statement
case $variable in
pattern1)
echo "Matched pattern1"
;;
pattern2)
echo "Matched pattern2"
;;
*)
echo "Default case"
;;
esac
Case Statement Patterns
# Multiple patterns
case $input in
y|Y|yes|Yes|YES)
echo "User said yes"
;;
n|N|no|No|NO)
echo "User said no"
;;
*)
echo "Invalid input"
;;
esac
# Pattern matching
case $filename in
*.txt)
echo "Text file"
;;
*.jpg|*.png|*.gif)
echo "Image file"
;;
[0-9]*.log)
echo "Log file starting with digit"
;;
*)
echo "Unknown file type"
;;
esac
# Range patterns
case $grade in
[90-100])
echo "A"
;;
[80-89])
echo "B"
;;
[70-79])
echo "C"
;;
*)
echo "F"
;;
esac
Loops
For Loops
# Basic for loop
for i in 1 2 3 4 5; do
echo "Number: $i"
done
# For loop with sequence
for i in {1..10}; do
echo "Count: $i"
done
# For loop with step
for i in {1..10..2}; do
echo "Odd number: $i"
done
# For loop with files
for file in *.txt; do
echo "Processing: $file"
done
# For loop with command substitution
for user in $(cut -d: -f1 /etc/passwd); do
echo "User: $user"
done
# For loop with arrays
fruits=("apple" "banana" "cherry")
for fruit in "${fruits[@]}"; do
echo "Fruit: $fruit"
done
C-style For Loop
# C-style for loop
for ((i=1; i<=10; i++)); do
echo "Counter: $i"
done
# Multiple variables
for ((i=1, j=10; i<=j; i++, j--)); do
echo "i=$i, j=$j"
done
# Infinite loop
for ((;;)); do
echo "Press Ctrl+C to exit"
sleep 1
done
While Loops
# Basic while loop
counter=1
while [ $counter -le 5 ]; do
echo "Count: $counter"
((counter++))
done
# While reading from file
while read line; do
echo "Line: $line"
done < file.txt
# While with condition
while [ -f "running.flag" ]; do
echo "Process is running..."
sleep 1
done
# Infinite while loop
while true; do
echo "Press Ctrl+C to exit"
sleep 1
done
Until Loops
# Basic until loop
counter=1
until [ $counter -gt 5 ]; do
echo "Count: $counter"
((counter++))
done
# Until with condition
until [ -f "stop.flag" ]; do
echo "Working..."
sleep 1
done
Loop Control
# Break and continue
for i in {1..10}; do
if [ $i -eq 5 ]; then
continue # Skip iteration
fi
if [ $i -eq 8 ]; then
break # Exit loop
fi
echo "Number: $i"
done
# Break/continue with nested loops
for i in {1..3}; do
for j in {1..3}; do
if [ $j -eq 2 ]; then
break 2 # Break out of both loops
fi
echo "i=$i, j=$j"
done
done
Functions
Basic Function Definition
# Function definition
function greet() {
echo "Hello, $1!"
}
# Alternative syntax
greet() {
echo "Hello, $1!"
}
# Function call
greet "World"
Function Parameters
# Function with parameters
calculate() {
local num1=$1
local num2=$2
local operation=$3
case $operation in
add)
echo $((num1 + num2))
;;
subtract)
echo $((num1 - num2))
;;
multiply)
echo $((num1 * num2))
;;
*)
echo "Unknown operation"
return 1
;;
esac
}
# Function call
result=$(calculate 10 5 add)
echo "Result: $result"
Function Variables
# Local variables
my_function() {
local local_var="I'm local"
global_var="I'm global"
echo "Inside function: $local_var"
echo "Inside function: $global_var"
}
# Variable scope
global_var="Initial value"
my_function
echo "Outside function: $global_var"
# echo "Outside function: $local_var" # This would cause an error
Return Values
# Return numeric value
is_even() {
local number=$1
if (( number % 2 == 0 )); then
return 0 # Success (true)
else
return 1 # Failure (false)
fi
}
# Using return value
if is_even 42; then
echo "42 is even"
else
echo "42 is odd"
fi
# Return string through echo
get_system_info() {
local os=$(uname -s)
local arch=$(uname -m)
echo "$os-$arch"
}
system_info=$(get_system_info)
echo "System: $system_info"
Function Arrays
# Function returning array
get_files() {
local directory=$1
local -a files
while IFS= read -r -d '' file; do
files+=("$file")
done < <(find "$directory" -type f -print0)
printf '%s\n' "${files[@]}"
}
# Using function with array
while IFS= read -r file; do
echo "File: $file"
done < <(get_files "/tmp")
Advanced Control Structures
Select Statement
# Menu selection
select option in "Option 1" "Option 2" "Option 3" "Quit"; do
case $option in
"Option 1")
echo "You selected Option 1"
;;
"Option 2")
echo "You selected Option 2"
;;
"Option 3")
echo "You selected Option 3"
;;
"Quit")
break
;;
*)
echo "Invalid option"
;;
esac
done
Nested Structures
# Nested loops and conditions
for file in *.txt; do
if [ -f "$file" ]; then
echo "Processing: $file"
while read line; do
if [[ $line == *"pattern"* ]]; then
echo "Found pattern in: $line"
fi
done < "$file"
fi
done
Error Handling
Exit Status
# Check command success
if command; then
echo "Command succeeded"
else
echo "Command failed with exit code $?"
fi
# Set exit status
my_function() {
if [ condition ]; then
return 0 # Success
else
return 1 # Failure
fi
}
Trap Signals
# Cleanup function
cleanup() {
echo "Cleaning up..."
rm -f temp_file.txt
exit 1
}
# Trap signals
trap cleanup SIGINT SIGTERM
# Main script
while true; do
echo "Working... (Press Ctrl+C to exit)"
sleep 1
done
Error Handling in Functions
# Function with error handling
safe_copy() {
local source=$1
local dest=$2
if [ ! -f "$source" ]; then
echo "Error: Source file '$source' does not exist" >&2
return 1
fi
if ! cp "$source" "$dest"; then
echo "Error: Failed to copy '$source' to '$dest'" >&2
return 1
fi
echo "Successfully copied '$source' to '$dest'"
return 0
}
# Using function with error handling
if ! safe_copy "file1.txt" "file2.txt"; then
echo "Copy operation failed"
exit 1
fi
Best Practices
Function Organization
# Function documentation
# Description: Validates email address format
# Parameters: $1 - email address to validate
# Returns: 0 if valid, 1 if invalid
validate_email() {
local email=$1
local regex='^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
if [[ $email =~ $regex ]]; then
return 0
else
return 1
fi
}
Defensive Programming
# Check parameter count
my_function() {
if [ $# -ne 2 ]; then
echo "Usage: my_function <param1> <param2>" >&2
return 1
fi
local param1=$1
local param2=$2
# Function logic here
}
# Validate input
process_number() {
local input=$1
if ! [[ $input =~ ^[0-9]+$ ]]; then
echo "Error: '$input' is not a valid number" >&2
return 1
fi
# Process the number
echo "Processing number: $input"
}
Performance Considerations
# Use appropriate loop types
# For known ranges: for i in {1..100}
# For file processing: while read
# For arrays: for item in "${array[@]}"
# Avoid unnecessary subshells
# Bad: result=$(echo $var | sed 's/old/new/')
# Good: result=${var/old/new}
# Use built-in tests
# Bad: if [ $(echo $var | wc -c) -gt 10 ]
# Good: if [ $\{#var} -gt 10 ]