← Back to Bash Course | Chapter 3: Control Flow | Lesson 14 of 14

Nested and Compound Conditionals

Real-world scripts often need to check several related conditions at once, either by nesting if-blocks inside each other or by combining conditions in a single test.

Combining Conditions in One [[ ]]

Multiple conditions can be combined directly inside a single [[ ]] test using && and ||, avoiding the need for nested if blocks when the checks are independent. This keeps related logic visually grouped together.

Example: Combining Conditions in One [[ ]]

bash
#!/bin/bash
age=25
has_ticket=true
if [[ $age -ge 18 && $has_ticket = true ]]; then
    echo "Entry granted"
else
    echo "Entry denied"
fi

Grouping with Parentheses

Inside [[ ]], parentheses ( ) can group sub-expressions to control evaluation order explicitly, similar to math expressions. Without grouping, mixing && and || can be evaluated in an order you didn't intend.

Example: Grouping with Parentheses

bash
#!/bin/bash
role="editor"
is_owner=false
if [[ ($role = "admin" || $role = "editor") && $is_owner = false ]]; then
    echo "Limited edit access granted"
fi

When Nesting if Is Clearer

Sometimes a later condition only makes sense to evaluate after an earlier one already passed, especially when the second check would error out or be meaningless otherwise (like checking a file's contents only after confirming the file exists). In these cases, nested if blocks are clearer than cramming everything into one compound expression.

Example: When Nesting if Is Clearer

bash
#!/bin/bash
file="data.txt"
echo "sample" > "$file"
if [ -f "$file" ]; then
    if [ -s "$file" ]; then
        echo "$file exists and is non-empty"
    else
        echo "$file exists but is empty"
    fi
else
    echo "$file does not exist"
fi

Flattening Deep Nesting with Guard Clauses

Instead of nesting many if blocks, checking for the failure cases first and exiting/continuing early (a 'guard clause') often produces flatter, more readable code. Each guard handles one specific unwanted condition before the main logic runs.

Example: Flattening Deep Nesting with Guard Clauses

bash
#!/bin/bash
check_input() {
    local value="$1"
    if [ -z "$value" ]; then
        echo "Rejected: empty value"
        return 1
    fi
    if [ "$value" -lt 0 ] 2>/dev/null; then
        echo "Rejected: negative value"
        return 1
    fi
    echo "Accepted: $value"
}
check_input ""
check_input "-5"
check_input "42"
Common Mistakes
  1. Over-nesting if blocks many levels deep when a single compound condition with &&/|| inside [[ ]] would be far more readable.
  2. Forgetting operator precedence when mixing && and || without parentheses; [[ ]] respects grouping with ( ) inside the double brackets, and skipping it can silently change which conditions are actually being checked together.
  3. Using [ ] for compound conditions with -a/-o; these are deprecated and behave unreliably with complex expressions -- [[ ]] with &&/|| is the modern, safer choice.
Chapter Summary
  • Conditions can be combined in one [[ ]] using && (and), || (or), and grouped with ( ) for explicit precedence.
  • Nested if blocks are appropriate when later checks only make sense/are only safe after earlier ones already passed.
  • [[ ]] supports parentheses for grouping sub-expressions; [ ] does not support this reliably, which is one more reason to prefer [[ ]] for compound logic.
  • Deeply nested conditionals usually indicate the logic can be flattened using compound &&/|| expressions or early return/continue guards.

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.