Control Flow

Master conditional statements and loops in Bash. This page will also introduce you to Regular Expressions in Bash.

If Statements

Conditional execution based on test conditions:

#!/bin/bash

# Basic if statement
if [ $age -ge 18 ]; then
    echo "Adult"
fi

# If-else
if [ $score -ge 60 ]; then
    echo "Pass"
else
    echo "Fail"
fi

# If-elif-else
if [ $grade -ge 90 ]; then
    echo "A"
elif [ $grade -ge 80 ]; then
    echo "B"
elif [ $grade -ge 70 ]; then
    echo "C"
else
    echo "F"
fi
💡 Note: Always use spaces inside square brackets: [ $var -eq 5 ] not [$var -eq 5]

Test Operators

Integer Comparisons

-eq    # Equal to
-ne    # Not equal to
-gt    # Greater than
-lt    # Less than
-ge    # Greater than or equal to
-le    # Less than or equal to

# Examples
[ $a -eq $b ]    # Is a equal to b?
[ $x -gt 10 ]    # Is x greater than 10?

String Comparisons

BASH
=      # Equal to
!=     # Not equal to
-z     # String is empty
-n     # String is not empty
<      # Less than (alphabetically)
>      # Greater than (alphabetically)

# Examples
[ "$str1" = "$str2" ]    # Are strings equal?
[ -z "$str" ]            # Is string empty?
[ -n "$str" ]            # Is string not empty?

File Tests

-e     # File exists
-f     # Is a regular file
-d     # Is a directory
-r     # File is readable
-w     # File is writable
-x     # File is executable
-s     # File is not empty

# Examples
if [ -f "config.txt" ]; then
    echo "Config file exists"
fi

if [ -d "/var/log" ]; then
    echo "Directory exists"
fi

if [ ! -e "file.txt" ]; then
    echo "File does not exist"
fi

Logical Operators

BASH
&&     # AND
||     # OR
!      # NOT

# Examples
if [ $age -ge 18 ] && [ $age -le 65 ]; then
    echo "Working age"
fi

if [ $x -lt 0 ] || [ $x -gt 100 ]; then
    echo "Out of range"
fi

if [ ! -d "backup" ]; then
    mkdir backup
fi

Modern Test Syntax [[ ]]

The double bracket syntax provides more features and is generally preferred:

# Pattern matching
if [[ $filename == *.txt ]]; then
    echo "Text file"
fi

# Regular expressions
if [[ $email =~ ^[a-zA-Z0-9]+@[a-zA-Z0-9]+\.[a-zA-Z]{2,}$ ]]; then
    echo "Valid email format"
fi

# No word splitting (safer)
if [[ $var == "hello world" ]]; then
    echo "Match"
fi

# Logical operators (easier to read)
if [[ $age -ge 18 && $citizen == "yes" ]]; then
    echo "Can vote"
fi

Case Statements

Multi-way branching for cleaner code:

BASH
#!/bin/bash

read -p "Enter a letter: " letter

case $letter in
    [aeiou])
        echo "Vowel"
        ;;
    [0-9])
        echo "Digit"
        ;;
    [A-Z])
        echo "Uppercase letter"
        ;;
    *)
        echo "Other character"
        ;;
esac
#!/bin/bash
# Menu system

echo "Select an option:"
echo "1) Start service"
echo "2) Stop service"
echo "3) Restart service"
echo "4) Exit"

read -p "Choice: " choice

case $choice in
    1|start)
        echo "Starting service..."
        ;;
    2|stop)
        echo "Stopping service..."
        ;;
    3|restart)
        echo "Restarting service..."
        ;;
    4|exit|quit)
        echo "Goodbye!"
        exit 0
        ;;
    *)
        echo "Invalid option"
        exit 1
        ;;
esac

For Loops

Basic For Loop

# Loop over a list
for item in apple banana cherry; do
    echo "Fruit: $item"
done

# Loop over command output
for file in *.txt; do
    echo "Processing $file"
    cat "$file"
done

# Loop over array
fruits=("apple" "banana" "cherry")
for fruit in "${fruits[@]}"; do
    echo "$fruit"
done

C-Style For Loop

BASH
# Traditional for loop
for ((i=0; i<10; i++)); do
    echo "Number: $i"
done

# Countdown
for ((i=10; i>=1; i--)); do
    echo "$i"
    sleep 1
done
echo "Liftoff!"

Looping Through Files

# Safe way to loop through files with spaces
while IFS= read -r -d '' file; do
    echo "Found: $file"
done < <(find . -name "*.log" -print0)

# Simple file loop
for file in /var/log/*.log; do
    if [ -f "$file" ]; then
        echo "Log file: $file"
    fi
done

While Loops

Execute commands while a condition is true:

# Basic while loop
counter=1
while [ $counter -le 5 ]; do
    echo "Count: $counter"
    ((counter++))
done

# Read file line by line
while IFS= read -r line; do
    echo "Line: $line"
done < input.txt

# Infinite loop with break
while true; do
    read -p "Enter command (quit to exit): " cmd
    if [ "$cmd" = "quit" ]; then
        break
    fi
    echo "You entered: $cmd"
done
#!/bin/bash
# Wait for file to appear

echo "Waiting for file.txt..."
while [ ! -f "file.txt" ]; do
    sleep 1
    echo "Still waiting..."
done
echo "File found!"
            

Until Loops

Execute commands until a condition becomes true:

BASH
# Basic until loop (opposite of while)
counter=1
until [ $counter -gt 5 ]; do
    echo "Count: $counter"
    ((counter++))
done

# Wait until service is ready
until curl -s http://localhost:8080 > /dev/null; do
    echo "Waiting for service..."
    sleep 2
done
echo "Service is ready!"

Loop Control

# break - exit the loop
for i in {1..10}; do
    if [ $i -eq 5 ]; then
        break  # Exit loop when i is 5
    fi
    echo $i
done

# continue - skip to next iteration
for i in {1..10}; do
    if [ $((i % 2)) -eq 0 ]; then
        continue  # Skip even numbers
    fi
    echo $i  # Only prints odd numbers
done

# Nested loops with break
for i in {1..3}; do
    for j in {1..3}; do
        if [ $j -eq 2 ]; then
            break  # Only breaks inner loop
        fi
        echo "$i,$j"
    done
done

Select Menu

Create interactive menus easily:

#!/bin/bash

echo "Choose your favorite fruit:"
select fruit in "Apple" "Banana" "Cherry" "Quit"; do
    case $fruit in
        "Apple")
            echo "You chose Apple"
            ;;
        "Banana")
            echo "You chose Banana"
            ;;
        "Cherry")
            echo "You chose Cherry"
            ;;
        "Quit")
            echo "Goodbye!"
            break
            ;;
        *)
            echo "Invalid option $REPLY"
            ;;
    esac
done

1. What is a Regular Expression (regex)?

A regular expression is a sequence of characters that forms a search pattern, commonly used for string matching, extraction, and text validation.

2. Regex in Bash Scripting

  • Bash supports regex in the [[ string =~ regex ]] conditional expression.
  • Regex is also widely used in tools like grep, sed, awk.
Note: Bash uses "extended regular expressions" (ERE) in =~, similar to egrep.

3. Basic Syntax Examples

  • In Bash conditionals:
    text="hello2024"
    if [[ $text =~ [a-z]+[0-9]+ ]]; then
      echo "It matches!"
    fi
    
  • With grep:
    grep "^[A-Z][a-z]+" names.txt

4. Common Regex Patterns

Pattern Meaning
.Any single character
[abc]Any character a, b, or c
[^abc]Any character NOT a, b, or c
[a-z]Any lowercase letter
[0-9]Any digit
+One or more (e.g., ab+ matches ab, abb, abbb)
*Zero or more
?Zero or one
^Start of line
$End of line
(...)Capture group
|Or (alternation)

5. Practical Bash Regex Matching Example

#!/bin/bash

email="test@linux.org"
pattern='^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'

if [[ $email =~ $pattern ]]; then
  echo "Valid email address"
else
  echo "Invalid email"
fi
  • Double brackets [[ ... ]] required for regex matching.
  • No need to quote the pattern variable with =~.

6. Regex with grep and sed

  • Search lines containing a phone number:
    grep -E '[0-9]{3}-[0-9]{3}-[0-9]{4}' contacts.txt
  • Replace digits with "X" in a file:
    sed -E 's/[0-9]/X/g' secret.txt

7. Extracting regex matches in Bash

line="My phone: 555-1212"
if [[ $line =~ ([0-9]{3})-([0-9]{4}) ]]; then
  echo "Area: ${BASH_REMATCH[1]}, Number: ${BASH_REMATCH[2]}"
fi
  • BASH_REMATCH contains matched substrings and capture groups.

8. Perl and Python One-Liners for Bash Scripting

Perl and Python one-liners are powerful tools for manipulating text, processing data, and performing quick operations directly from the command line, often used as part of Bash scripts. Here are some useful examples to help streamline your workflow:

Perl One-Liners

  • Print only lines matching a pattern:
    perl -ne 'print if /pattern/' filename
  • Replace text in a file (in-place):
    perl -pi -e 's/old/new/g' filename
  • Print line numbers with lines:
    perl -ne 'print "$. $_"' filename
  • Sum numbers in a column:
    perl -lane '$sum += $F[0]; END { print $sum }' filename

Python One-Liners

  • Print lines containing a pattern:
    python3 -c "import sys; [print(line, end='') for line in sys.stdin if 'pattern' in line]" < filename
  • Replace text in a file (output to stdout):
    python3 -c "import sys; [print(line.replace('old','new'), end='') for line in sys.stdin]" < filename
  • Count unique lines:
    python3 -c "import sys; print(len(set(sys.stdin)))" < filename
  • Sum numbers in a column:
    python3 -c "import sys; print(sum(float(line.split()[0]) for line in sys.stdin))" < filename

These one-liners can be embedded directly within Bash scripts or used in pipelines to process data efficiently. Be sure to adjust patterns and column indices to suit your specific use cases.

9. Resources