Course Outline (Part 7)

In Part 7 of the Linux Bash Course, we cover the logic systems that control script execution. You will learn legacy and modern command arithmetic methods, how to calculate floating-point numbers with bc, how to build conditional ladders, test file properties, and execute various loop patterns.

For deep-dive rules on conditional expressions, see the GNU Bash Manual - Conditional Expressions.


Chapter 19: Arithmetic & Expressions

19.1 The expr Command (Legacy)

expr is an external utility used in legacy Bourne shells to evaluate expressions. It only supports basic integer arithmetic and requires spaces between numbers and operators. Multiplication signs (*) must be escaped to prevent shell wildcard expansion.

  • Syntax:
    expr value1 operator value2
  • Example Command:
    expr 10 \* 5
  • Expected Output:
    50
  • Flag & Command Breakdown:
    • \*: The multiplication symbol must be escaped with a backslash in the terminal to prevent the shell from interpreting it as a wildcard file expansion pattern.

19.2 Double Parentheses: $(( ... ))

Modern Bash scripts use double parentheses $(( ... )) for integer calculations. This syntax runs faster than expr because it is built directly into the shell process, and it does not require escaping operators or spacing out components.

  • Syntax:
    result=$((expression))
  • Example Command:
    NUM1=20
    NUM2=5
    RESULT=$((NUM1 * NUM2 + 10))
    echo "Calculated: $RESULT"
  • Expected Output:
    Calculated: 110

19.3 Incrementing and Decrementing: ((i++))

Inside arithmetic brackets, you can perform post/pre-incrementing and post/pre-decrementing using C-style math notations. This is commonly used in loop counters.

  • Syntax:
    ((variable++))
    ((variable--))
  • Example Command:
    COUNTER=5
    ((COUNTER++))
    echo "Post-increment: $COUNTER"
    ((COUNTER--))
    echo "Post-decrement: $COUNTER"
  • Expected Output:
    Post-increment: 6
    Post-decrement: 5

19.4 The let Command

let is a Bash shell built-in command that evaluates math expressions and directly assigns the result to a variable. Like $(( )), it only supports integer arithmetic.

  • Syntax:
    let "variable = expression"
  • Example Command:
    let "SUM = 10 + 20" "PROD = SUM * 2"
    echo "Sum: $SUM, Product: $PROD"
  • Expected Output:
    Sum: 30, Product: 60

19.5 Arithmetic with bc (Floating Point)

Bash cannot natively compute floating-point (decimal) numbers; $(( 5 / 2 )) yields 2. To solve this, we pipe mathematical statements into bc (An arbitrary precision calculator language utility).

  • Syntax:
    echo "expression" | bc
  • Example Command:
    echo "5 / 2" | bc
    echo "5 / 2.0" | bc -l
  • Expected Output:
    2
    2.50000000000000000000
  • Flag & Command Breakdown:
    • -l: Loads the standard math library, which sets the default precision (scale) to 20 decimal places.

19.6 Setting Precision in bc

To limit decimal precision when using bc, prepend the equation with the scale variable definition followed by a semicolon.

  • Syntax:
    echo "scale=decimal_places; expression" | bc
  • Example Command:
    echo "scale=2; 10 / 3" | bc
  • Expected Output:
    3.33

19.7 Hexadecimal and Octal Math

You can perform math in different bases by defining input/output bases using the ibase and obase settings inside the bc utility.

  • Syntax:
    echo "ibase=input_base; obase=output_base; expression" | bc
  • Example Command:
    # Convert Hexadecimal FF to decimal
    echo "ibase=16; FF" | bc
    # Convert decimal 15 to binary
    echo "obase=2; 15" | bc
  • Expected Output:
    255
    1111

19.8 Random Number Generation: $RANDOM

$RANDOM is a built-in shell variable that returns a pseudo-random integer between 0 and 32767 each time it is accessed. To scale this number to a specific range (e.g., between 1 and 10), use the modulo operator (%).

  • Syntax:
    $(( RANDOM % range + offset ))
  • Example Command:
    # Generate random number between 1 and 100
    RAND_VAL=$(( (RANDOM % 100) + 1 ))
    echo "Generated: $RAND_VAL"
  • Expected Output:
    Generated: 42

19.9 Sequencing with seq

The seq utility generates a sequence of numbers from a specified start value to an end value, with an optional step value.

  • Syntax:
    seq [start] [step] [end]
  • Example Command:
    seq 1 2 5
  • Expected Output:
    1
    3
    5

19.10 Evaluating Expressions: [[ ... ]]

In modern Bash, double square brackets [[ ... ]] are used to evaluate expressions and test conditions. This is a shell keyword, which makes it safer and more feature-rich than the legacy POSIX [ ... ] command.

  • Syntax:
    [[ condition ]]
  • Example Command:
    STR="tech"
    [[ -n "$STR" ]]
    echo $?
  • Expected Output:
    0
  • Flag & Command Breakdown:
    • -n: Checks if the string length is non-zero. Returns 0 (true) if it contains characters.

Chapter 20: Conditional Statements (if/else)

20.1 Syntax of if-then

The if statement executes a block of code only if the specified command or test expression returns an exit status of 0 (success).

  • Syntax:
    if [[ condition ]]; then
        # commands
    fi
  • Example Command:
    VAL=10
    if [[ $VAL -eq 10 ]]; then
        echo "Condition Met"
    fi
  • Expected Output:
    Condition Met

20.2 if-then-else

The else block executes alternative commands if the test expression returns a non-zero exit status.

  • Syntax:
    if [[ condition ]]; then
        # commands when true
    else
        # commands when false
    fi
  • Example Command:
    VAL=5
    if [[ $VAL -eq 10 ]]; then
        echo "Ten"
    else
        echo "Not Ten"
    fi
  • Expected Output:
    Not Ten

20.3 if-elif-else Ladder

To evaluate multiple conditions in sequence, chain them using the elif (else-if) block.

  • Syntax:
    if [[ cond1 ]]; then
        # commands
    elif [[ cond2 ]]; then
        # commands
    else
        # commands
    fi
  • Example Command:
    SCORE=85
    if [[ $SCORE -ge 90 ]]; then
        echo "Grade A"
    elif [[ $SCORE -ge 80 ]]; then
        echo "Grade B"
    else
        echo "Grade C"
    fi
  • Expected Output:
    Grade B

20.4 Nested if Statements

You can nest if statements inside another if block to evaluate dependent conditions.

  • Example Script:
    #!/bin/bash
    USER_TYPE="admin"
    LOGGED_IN=true
    
    if [[ "$LOGGED_IN" = true ]]; then
        if [[ "$USER_TYPE" = "admin" ]]; then
            echo "Access granted: Admin Dashboard."
        else
            echo "Access granted: Standard Portal."
        fi
    else
        echo "Error: Authentication required."
    fi

20.5 Test Command: [ ] vs [[ ]]

  • [ ] (Single Brackets): Runs the POSIX /usr/bin/test utility. It requires careful quoting of variables to avoid syntax errors from word splitting and path expansion.

  • [[ ]] (Double Brackets): An extended shell keyword supported by Bash, Zsh, and Ksh. It handles empty variables and file wildcards safely, and it supports pattern matching (==) and regular expressions (=~) without throwing errors.

  • Example Comparison:

    EMPTY_VAR=""
    # This throws a syntax error:
    [ $EMPTY_VAR = "test" ]
    # This evaluates safely without throwing errors:
    [[ $EMPTY_VAR = "test" ]]

20.6 String Comparisons (=, !=, -z)

Common string test operators inside [[ ]]:

  • =: Checks if strings are equal.

  • !=: Checks if strings are not equal.

  • -z: Checks if the string is empty (length is zero).

  • -n: Checks if the string is not empty.

  • Syntax:

    [[ "$string1" = "$string2" ]]
    [[ -z "$string" ]]
  • Example Command:

    USER_ROLE="developer"
    if [[ -z "$USER_ROLE" ]]; then
        echo "Role is unset"
    elif [[ "$USER_ROLE" = "developer" ]]; then
        echo "Access: IDE"
    fi
  • Expected Output:

    Access: IDE

20.7 Numeric Comparisons (-eq, -lt, -gt)

Because Bash handles double brackets as string tests by default, you must use specific operators for mathematical comparisons:

  • -eq: Equal to

  • -ne: Not equal to

  • -lt: Less than

  • -le: Less than or equal to

  • -gt: Greater than

  • -ge: Greater than or equal to

  • Syntax:

    [[ num1 -eq num2 ]]
  • Example Command:

    LIMIT=100
    COUNT=80
    if [[ $COUNT -lt $LIMIT ]]; then
        echo "Under limit"
    fi
  • Expected Output:

    Under limit

20.8 File Tests (-f, -d, -e, -x)

File test operators check files and folders on the filesystem:

  • -e: Checks if the file or folder exists.

  • -f: Checks if it exists and is a regular file.

  • -d: Checks if it exists and is a directory.

  • -r: Checks if the file is readable.

  • -w: Checks if the file is writable.

  • -x: Checks if the file is executable.

  • Syntax:

    [[ -f /path/to/file ]]
  • Example Command:

    TARGET="/etc/passwd"
    if [[ -f "$TARGET" ]]; then
        echo "$TARGET exists and is a regular file."
    fi
  • Expected Output:

    /etc/passwd exists and is a regular file.

20.9 Logical AND (&&) and OR (||)

You can combine multiple test conditions inside double brackets:

  • &&: Logical AND. Both conditions must be true.

  • ||: Logical OR. At least one condition must be true.

  • Syntax:

    [[ cond1 && cond2 ]]
    [[ cond1 || cond2 ]]
  • Example Command:

    USER="root"
    SYS_LOAD=5
    if [[ "$USER" = "root" && $SYS_LOAD -lt 10 ]]; then
        echo "System admin access approved."
    fi
  • Expected Output:

    System admin access approved.

20.10 Using case for Multiple Conditions

The case statement is a switch-like structure. It evaluates a value against multiple patterns and executes the block associated with the first match. Patterns can include wildcards (like * or ?) and the pipe symbol (|) for logical OR. Each block must end with double semicolons (;;).

  • Syntax:
    case $variable in
        pattern1)
            # commands
            ;;
        pattern2|pattern3)
            # commands
            ;;
        *)
            # default commands
            ;;
    esac
  • Example Command:
    OS_TYPE="Ubuntu"
    case "$OS_TYPE" in
        "Ubuntu"|"Debian")
            echo "Installer: apt"
            ;;
        "CentOS"|"RHEL")
            echo "Installer: yum"
            ;;
        *)
            echo "Installer unknown"
            ;;
    esac
  • Expected Output:
    Installer: apt

Chapter 21: Loops in Bash

21.1 for Loop over a List

The standard for loop iterates over a list of space-separated items, executing the loop body once for each element.

  • Syntax:
    for item in list; do
        # commands
    done
  • Example Command:
    for server in web01 database cache01; do
        echo "Deploying to: $server"
    done
  • Expected Output:
    Deploying to: web01
    Deploying to: database
    Deploying to: cache01

21.2 for Loop with Range ({1..10})

You can use brace expansion {start..end..step} to generate ranges of numbers or characters dynamically.

  • Syntax:
    for i in {start..end..step}; do
        # commands
    done
  • Example Command:
    for i in {2..6..2}; do
        echo "Count: $i"
    done
  • Expected Output:
    Count: 2
    Count: 4
    Count: 6

21.3 C-Style for Loop

The C-style for loop is ideal for mathematical loop counters. It uses a three-expression structure: initialization, evaluation, and step increment.

  • Syntax:
    for ((init; condition; step)); do
        # commands
    done
  • Example Command:
    for ((i=1; i<=3; i++)); do
        echo "Iteration: $i"
    done
  • Expected Output:
    Iteration: 1
    Iteration: 2
    Iteration: 3

21.4 The while Loop

The while loop runs repeatedly as long as the test condition returns an exit status of 0 (success).

  • Syntax:
    while [[ condition ]]; do
        # commands
    done
  • Example Command:
    COUNTER=3
    while [[ $COUNTER -gt 0 ]]; do
        echo "Tick: $COUNTER"
        ((COUNTER--))
    done
  • Expected Output:
    Tick: 3
    Tick: 2
    Tick: 1

21.5 Infinite Loops (while true)

An infinite loop runs indefinitely unless it encounters a break statement or is terminated by an external process signal. The built-in commands true and : always return success.

  • Syntax:
    while true; do
        # commands
    done
  • Example Script:
    #!/bin/bash
    while true; do
        echo "Monitoring process logs..."
        sleep 5
    done

21.6 The until Loop (Opposite of while)

Unlike while, the until loop executes repeatedly as long as the test condition returns a failure status (non-zero). It terminates when the condition evaluates to success (0).

  • Syntax:
    until [[ condition ]]; do
        # commands
    done
  • Example Command:
    COUNT=1
    until [[ $COUNT -gt 3 ]]; do
        echo "Count: $COUNT"
        ((COUNT++))
    done
  • Expected Output:
    Count: 1
    Count: 2
    Count: 3

21.7 Reading File Lines with while read

Combining a while loop with the read command allows you to process text files line-by-line in a stream.

  • Syntax:
    while read -r variable; do
        # commands
    done < file.txt
  • Example Command:
    printf "Service A\nService B\n" > status.log
    while read -r service; do
        echo "Status check: $service"
    done < status.log
  • Expected Output:
    Status check: Service A
    Status check: Service B

21.8 Loop Control: break

The break statement exits the active loop immediately, skipping any remaining code in the loop body.

  • Syntax:
    break
  • Example Command:
    for val in 1 2 3 4 5; do
        if [[ $val -eq 3 ]]; then
            break
        fi
        echo "Val: $val"
    done
  • Expected Output:
    Val: 1
    Val: 2

21.9 Loop Control: continue

The continue statement skips the rest of the current loop iteration and immediately starts the next one.

  • Syntax:
    continue
  • Example Command:
    for val in 1 2 3; do
        if [[ $val -eq 2 ]]; then
            continue
        fi
        echo "Val: $val"
    done
  • Expected Output:
    Val: 1
    Val: 3

21.10 Nested Loops

You can run loops inside other loops to iterate over multidimensional grids or nested directories.

  • Example Command:
    for x in A B; do
        for y in 1 2; do
            echo "Coordinates: $x$y"
        done
    done
  • Expected Output:
    Coordinates: A1
    Coordinates: A2
    Coordinates: B1
    Coordinates: B2