How to Test Multiple Conditions
Quick Answer: Test Multiple Conditions in Bash
To combine conditions, use && for AND logic: [ condition1 ] && [ condition2 ]. Use || for OR logic: [ condition1 ] || [ condition2 ]. Combine them for complex logic. Always quote variables: [ "$var" = "value" ] && echo "yes".
Quick Comparison: Condition Operators
| Operator | Meaning | Returns | Best For |
|---|---|---|---|
| && | AND | True if all true | All conditions needed |
| || | OR | True if any true | Fallback/alternative |
| [ ] && [ ] | Test syntax | True/false | Simple conditions |
| [[ ]] && [[ ]] | Extended test | True/false | Advanced patterns |
Bottom line: Use && for AND; use || for OR. Chain them together for complex logic.
Why Test Multiple Conditions?
Many real-world scripts need to check multiple conditions before taking action. For example, you might want to run a backup only if the source exists AND the backup drive is mounted AND there’s enough free space. That requires testing multiple conditions together.
Bash gives you several ways to combine conditions, each with different semantics. Learning these patterns is fundamental to writing robust scripts.
Method 1: The AND Operator (&&)
The && operator means “only run the next command if the previous command succeeded”. It’s useful for chaining operations that depend on each other:
# Simple chaining
$ mkdir /tmp/test && cd /tmp/test && touch file.txt
# The directory is created, then we enter it, then create a file
# Each command only runs if the previous succeeded
$ cd /nonexistent && echo "This won't print"
bash: cd: /nonexistent: No such file or directory
# The echo never runs because cd failed
In an if statement, you can use && to check multiple conditions:
#!/bin/bash
filename="$1"
# Check if file exists AND is readable AND contains "password"
if [ -f "$filename" ] && [ -r "$filename" ] && grep -q "password" "$filename"; then
echo "File contains password entries"
else
echo "File check failed"
fi
This is equivalent to nesting if statements:
if [ -f "$filename" ]; then
if [ -r "$filename" ]; then
if grep -q "password" "$filename"; then
echo "File contains password entries"
fi
fi
fi
The && version is much cleaner.
The OR Operator (||)
The || operator means “if the previous command failed, run the next one”. It’s useful for fallbacks and error handling:
# Try to use a command, fall back to another if it fails
$ which python || which python3 || echo "Python not found"
/usr/bin/python3
# Try to cd to directory, exit if it fails
$ cd /backups || exit 1
In an if statement, || lets you say “if this condition is false OR this one is false”:
#!/bin/bash
username="$1"
# Allow the script if user is root OR if user is in admin group
if [ "$EUID" -eq 0 ] || groups "$username" | grep -q "^admin"; then
echo "User has permission"
else
echo "User does not have permission"
exit 1
fi
Combining && and ||
You can chain them together for sophisticated logic:
#!/bin/bash
# Try to update, if that fails, notify admin
apt-get update && apt-get upgrade || mail -s "Update failed" admin@example.com
# Try method 1, if it fails try method 2, if that fails give up
mkdir /tmp/test && touch /tmp/test/file.txt || touch ~/file.txt || echo "Failed to create file"
Using the test Command with Multiple Conditions
Bash’s [ command (which is an alias for test) lets you combine conditions using -a (AND) and -o (OR):
# Using -a for AND
if [ -f "$file" -a -r "$file" ]; then
echo "File exists and is readable"
fi
# Using -o for OR
if [ -d "$path" -o -L "$path" ]; then
echo "Path is either a directory or a symbolic link"
fi
# Combining both with parentheses (need escaping)
if [ \( -f "$file" \) -a \( -r "$file" \) ]; then
echo "File exists and is readable"
fi
However, modern Bash prefer the [[ ]] compound command with && and ||:
# Modern style (preferred)
if [[ -f "$file" && -r "$file" ]]; then
echo "File exists and is readable"
fi
if [[ -d "$path" || -L "$path" ]]; then
echo "Path is a directory or symbolic link"
fi
Practical Example: System Checker Script
Here’s a real-world script that checks multiple system conditions:
#!/bin/bash
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
NC='\033[0m' # No Color
echo "=== System Health Check ==="
# Check if running as root
if [[ $EUID -eq 0 ]]; then
echo -e "${GREEN}âś“ Running as root${NC}"
else
echo -e "${RED}âś— Not running as root${NC}"
fi
# Check disk space (more than 10% free)
DISK_USAGE=$(df / | tail -1 | awk '{print $5}' | sed 's/%//')
if [[ $DISK_USAGE -lt 90 ]]; then
echo -e "${GREEN}âś“ Disk usage OK (${DISK_USAGE}%)${NC}"
else
echo -e "${RED}âś— Disk nearly full (${DISK_USAGE}%)${NC}"
fi
# Check if essential services are running
if systemctl is-active --quiet sshd && systemctl is-active --quiet cron; then
echo -e "${GREEN}âś“ SSH and cron services running${NC}"
else
echo -e "${RED}âś— Some services not running${NC}"
fi
# Check if important files exist and are readable
CONFIG_FILE="/etc/myapp/config.conf"
if [[ -f "$CONFIG_FILE" && -r "$CONFIG_FILE" ]]; then
echo -e "${GREEN}âś“ Config file exists and is readable${NC}"
else
echo -e "${RED}âś— Config file missing or not readable${NC}"
fi
# Complex condition: user exists AND has home directory AND home is readable
USERNAME="appuser"
if id "$USERNAME" &>/dev/null && [[ -d "/home/$USERNAME" ]] && [[ -r "/home/$USERNAME" ]]; then
echo -e "${GREEN}âś“ App user configured properly${NC}"
else
echo -e "${RED}âś— App user not properly configured${NC}"
fi
echo "=== Check Complete ==="
Output:
=== System Health Check ===
âś“ Running as root
âś“ Disk usage OK (45%)
âś“ SSH and cron services running
âś“ Config file exists and is readable
âś“ App user configured properly
=== Check Complete ===
Example: File Processing with Multiple Conditions
#!/bin/bash
FILE="$1"
# Multiple conditions: file exists AND is regular file AND is readable AND is not empty
if [[ -f "$FILE" && -r "$FILE" && -s "$FILE" ]]; then
echo "Processing file: $FILE"
# Check if file is a text file (simple check)
if file "$FILE" | grep -q "text"; then
# Count lines AND show size
lines=$(wc -l < "$FILE")
size=$(du -h "$FILE" | cut -f1)
echo "Lines: $lines, Size: $size"
else
echo "File is not a text file"
exit 1
fi
else
if [[ ! -f "$FILE" ]]; then
echo "ERROR: File does not exist"
elif [[ ! -r "$FILE" ]]; then
echo "ERROR: File is not readable"
elif [[ ! -s "$FILE" ]]; then
echo "ERROR: File is empty"
fi
exit 1
fi
Quick Reference
| Operator | Usage | Meaning |
|---|---|---|
&& | cmd1 && cmd2 | Run cmd2 only if cmd1 succeeds |
|| | cmd1 || cmd2 | Run cmd2 only if cmd1 fails |
-a | [ cond1 -a cond2 ] | Both conditions true (AND) |
-o | [ cond1 -o cond2 ] | Either condition true (OR) |
&& | [[ cond1 && cond2 ]] | Both conditions true (modern style) |
|| | [[ cond1 || cond2 ]] | Either condition true (modern style) |
Common Patterns
Check if command exists AND is executable:
if command -v docker &>/dev/null && docker --version &>/dev/null; then
echo "Docker is installed and working"
fi
Check if directory exists AND is writable:
if [[ -d "$dir" && -w "$dir" ]]; then
echo "Can write to directory"
fi
Try multiple options, use first that works:
if [[ -f /etc/config ]] && grep -q "setting" /etc/config; then
source /etc/config
elif [[ -f ~/.config ]] && grep -q "setting" ~/.config; then
source ~/.config
else
echo "No valid config found"
exit 1
fi
Important Notes
&&and||check exit codes, not condition values- Use
[[ ]]in modern Bash for cleaner syntax - Parentheses can be used to group conditions:
[[ (cond1 || cond2) && cond3 ]] - The order matters: short-circuit evaluation stops as soon as the result is determined
- Use proper quoting when variables are involved:
[[ -z "$var" ]]
Summary
Testing multiple conditions is essential for writing robust scripts. Use && when you want to chain commands or ensure previous success, use || for fallbacks and error handling, and combine them for sophisticated logic. Master these patterns and your scripts will be more reliable and easier to understand.