Web Analytics

Logical Operators

Beginner ~15 min read

Logical operators let you combine multiple conditions and control command execution flow in Bash. You'll learn to use AND (&&), OR (||), and NOT (!) to build complex conditional logic, plus a powerful technique for command chaining based on success or failure!

Logical Operators in Conditions

Inside [[ ]] or (( )), use && (AND), || (OR), and ! (NOT) to combine conditions.

Output
Click Run to execute your code
Operator Name Description Example
&& AND True if BOTH conditions are true [[ $a -gt 5 && $b -lt 10 ]]
|| OR True if EITHER condition is true [[ $x == "yes" || $x == "y" ]]
! NOT Inverts the condition (true becomes false) [[ ! -f "$file" ]]
Operator Precedence: NOT (!) has the highest precedence, then AND (&&), then OR (||). Use parentheses to group conditions: [[ (cond1 || cond2) && cond3 ]].

Command Chaining with && and ||

Outside of [[ ]], these operators chain commands based on exit status. This is one of the most useful patterns in shell scripting!

Output
Click Run to execute your code
Pattern Behavior Use Case
cmd1 && cmd2 Run cmd2 only if cmd1 succeeds (exit 0) Sequential dependent tasks
cmd1 || cmd2 Run cmd2 only if cmd1 fails (exit non-0) Fallback/error handling
cmd && true || false Ternary-like: do one thing on success, another on failure Conditional actions
Pro Tip: The cmd && success_action || failure_action pattern is great for one-liners but can be tricky. If success_action itself fails, failure_action will also run! For complex logic, use proper if statements.

Short-Circuit Evaluation

Bash uses short-circuit evaluation, meaning it stops evaluating as soon as the result is determined. This is important for both performance and avoiding errors!

# AND short-circuits on first false
[[ false && echo "This won't run" ]]

# OR short-circuits on first true
[[ true || echo "This won't run" ]]

# Practical use: check variable exists before using
[[ -n "$file" && -f "$file" ]] && cat "$file"
# If file is empty/unset, the -f test never runs

# Safe division: check denominator first
denom=0
[[ $denom -ne 0 && $((10 / denom)) -gt 5 ]]
# Division never executes because first condition is false
Why It Matters:
  • Performance: Unnecessary checks are skipped
  • Safety: Prevents errors from invalid operations
  • Guard Pattern: Check existence before accessing ([[ -f $f && cat $f ]])

POSIX Operators: -a and -o (Legacy)

Inside [ ] (single brackets), the POSIX-compatible operators are -a (AND) and -o (OR). You'll see these in older scripts.

Output
Click Run to execute your code
Caution: The -a and -o operators are deprecated and can cause issues with certain inputs. Always prefer && and || inside [[ ]] for modern scripts. Only use -a and -o when writing strictly POSIX-compliant scripts for non-Bash shells.

Common Mistakes

1. Using && and || in single brackets [ ]

# Wrong - && is command chaining, not logical AND in [ ]
[ $a -gt 5 && $b -lt 10 ]  # Syntax error or wrong behavior

# Correct - use -a inside [ ]
[ $a -gt 5 -a $b -lt 10 ]

# Better - use [[ ]] with &&
[[ $a -gt 5 && $b -lt 10 ]]

2. Misunderstanding the ternary-like pattern

# Potentially wrong behavior
[[ condition ]] && echo "success" || echo "fail"
# If echo "success" fails somehow, "fail" also prints!

# Safer alternative for complex logic
if [[ condition ]]; then
    echo "success"
else
    echo "fail"
fi

3. Forgetting parentheses for precedence

# Confusing precedence
[[ $a == "x" || $b == "y" && $c == "z" ]]
# This is: a=="x" OR (b=="y" AND c=="z")

# Use parentheses for clarity
[[ ($a == "x" || $b == "y") && $c == "z" ]]
# This is: (a=="x" OR b=="y") AND c=="z"

Exercise: Access Control Script

Task: Create a script that checks multiple conditions for access control!

Requirements:

  • Check if user is "admin" OR user is "root"
  • Check if age is 18+ AND has permission flag
  • Use NOT to check if user is NOT banned
  • Use command chaining to create a log file only on success
Show Solution
#!/bin/bash
# Access Control Script

user="admin"
age=25
has_permission=true
banned_users="guest blocked"

echo "=== Access Control Check ==="
echo "User: $user, Age: $age, Has Permission: $has_permission"
echo ""

# Check 1: Admin OR Root
echo "--- Check 1: Admin or Root ---"
if [[ "$user" == "admin" || "$user" == "root" ]]; then
    echo "PASS: User is admin or root"
else
    echo "FAIL: User is not admin or root"
fi

# Check 2: Age AND Permission
echo ""
echo "--- Check 2: Age and Permission ---"
if [[ $age -ge 18 && "$has_permission" == "true" ]]; then
    echo "PASS: User is 18+ with permission"
else
    echo "FAIL: Age or permission check failed"
fi

# Check 3: NOT Banned
echo ""
echo "--- Check 3: Not Banned ---"
if [[ ! "$banned_users" =~ $user ]]; then
    echo "PASS: User is not banned"
else
    echo "FAIL: User is banned"
fi

# Command chaining: Log success
echo ""
echo "--- Command Chaining ---"
[[ "$user" == "admin" ]] && echo "Access granted" || echo "Access denied"

Summary

  • AND (&&): Both conditions must be true; in command chaining, runs second only if first succeeds
  • OR (||): Either condition can be true; in command chaining, runs second only if first fails
  • NOT (!): Inverts the condition's truth value
  • Short-Circuit: Evaluation stops when result is determined
  • Precedence: ! > && > || (use parentheses for clarity)
  • Legacy: -a and -o for POSIX compatibility (prefer && and ||)
  • Command Chaining: cmd1 && cmd2 || cmd3 for conditional execution

What's Next?

Now that you can combine conditions, let's learn about File Test Operators. You'll discover how to check if files exist, are readable, writable, executable, and much more. These are essential for any script that works with files!