Skip to main content

How to Test Multiple Conditions

• 3 min read
bash conditions operators && || logical if statements

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

OperatorMeaningReturnsBest For
&&ANDTrue if all trueAll conditions needed
||ORTrue if any trueFallback/alternative
[ ] && [ ]Test syntaxTrue/falseSimple conditions
[[ ]] && [[ ]]Extended testTrue/falseAdvanced 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

OperatorUsageMeaning
&&cmd1 && cmd2Run cmd2 only if cmd1 succeeds
||cmd1 || cmd2Run 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.