Logical Operators
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.
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" ]] |
!) 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!
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 |
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
- 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.
Click Run to execute your code
-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:
-aand-ofor POSIX compatibility (prefer&&and||) - Command Chaining:
cmd1 && cmd2 || cmd3for 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!
Enjoying these tutorials?