100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Bash

Conditionals in Bash (if, case)

Master Bash's conditional constructs — if/elif/else, test expressions, [[ ]] versus [ ], and case statements — to make scripts branch correctly on real-world conditions.

Bash Scripting FundamentalsBeginner9 min readJul 9, 2026
Analogies

Conditionals in Bash (if, case)

Bash conditionals let a script take different actions depending on the state of files, variables, command exit codes, or string and numeric comparisons. Unlike many languages, Bash's if doesn't evaluate a boolean expression directly — it runs a command and branches based on that command's exit status, where 0 means true/success and any non-zero value means false/failure. This is why test, [ ], and [[ ]] are themselves commands (or a builtin/keyword) that return an exit status representing the result of the comparison you wrote, and why any command at all — not just a comparison — can be used as an if condition.

🏏

Cricket analogy: Bash's if isn't evaluating true/false directly but checking a command's exit status, like an umpire's decision review where the outcome is judged purely by whether the third umpire's verdict comes back 'not out' (success) or 'out' (failure).

if / elif / else and exit-status-driven logic

The basic form is if command; then ...; elif command2; then ...; else ...; fi. Because if tests exit status, if grep -q ERROR logfile.txt; then is entirely valid and idiomatic — no explicit [ ] is needed when the condition is itself a command whose success or failure is meaningful. Logical operators && and || can also drive simple branching without a full if block, e.g. mkdir -p /data || { echo "failed"; exit 1; }, though for anything beyond a single action, an explicit if is more readable and less error-prone.

🏏

Cricket analogy: if grep -q ERROR logfile.txt; then is like a scout checking whether a bowler's spell contains any no-balls by scanning the over sheet silently, branching strategy only on whether any were found, no separate boolean needed.

bash
#!/usr/bin/env bash
set -euo pipefail

service="nginx"

if systemctl is-active --quiet "$service"; then
    echo "$service is running"
elif systemctl is-enabled --quiet "$service"; then
    echo "$service is enabled but not running; starting it"
    sudo systemctl start "$service"
else
    echo "$service is not installed or not enabled" >&2
    exit 1
fi

# Short-circuit form for a simple guard clause
[ -d /var/log/myapp ] || mkdir -p /var/log/myapp

[ ] versus [[ ]] and test operators

[ ] is the POSIX test command — portable across sh implementations but limited: it requires careful quoting of every variable, doesn't support &&/|| inside the brackets, and treats </> as redirection rather than comparison. [[ ]] is a Bash (and ksh/zsh) keyword with more forgiving parsing — unquoted variables inside [[ ]] won't word-split or glob, it supports &&, ||, and pattern matching with ==, and =~ enables full regular-expression matching. For portability to /bin/sh or dash-based scripts (common on Debian/Ubuntu where sh is dash), you must use [ ]; in Bash-only scripts, [[ ]] is generally safer and preferred.

🏏

Cricket analogy: Using [ ] versus [[ ]] is like the difference between a strict club-level umpire who needs every rule spelled out precisely (POSIX test, careful quoting) and a relaxed exhibition-match umpire who reads intent more forgivingly (Bash's [[ ]]).

bash
# File and string tests
if [[ -f /etc/nginx/nginx.conf ]]; then echo "config exists"; fi
if [[ -z "$1" ]]; then echo "argument is empty"; fi
if [[ "$env" == "production" ]]; then echo "prod mode"; fi

# Numeric comparison uses -eq, -lt, -gt, etc. inside [ ] / [[ ]]
if [[ "$count" -gt 100 ]]; then echo "threshold exceeded"; fi

# Regex matching (Bash-only, requires [[ ]])
if [[ "$version" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
    echo "valid semver: $version"
fi

# Combined conditions
if [[ -f "$1" && -r "$1" ]]; then
    echo "file exists and is readable"
fi

Numeric comparisons in [ ]/[[ ]] use -eq, -ne, -lt, -le, -gt, -ge rather than ==/</>, which are reserved for string comparison (and in plain [ ], </> are actually shell redirection operators, so they'd silently create files instead of comparing!). Inside (( )) arithmetic context, however, you can use the familiar ==, <, > operators directly, e.g. if (( count > 100 )); then.

A frequent mistake is writing if [ $var == production ] without quoting $var. If $var is empty, this collapses to if [ == production ], a syntax error test cannot parse. Always quote: if [ "$var" == "production" ], or switch to [[ ]], which tolerates unquoted variables far more gracefully (though quoting is still good habit there too).

case statements

case matches a value against a series of shell glob patterns, each terminated by ), with the matching block ending in ;;. It is more readable than a long if/elif chain when testing one variable against many possible values, and supports pattern alternation with | inside a single pattern, e.g. start|up). case is commonly used to parse the first positional argument of a script (like a subcommand) or to branch on OS detection.

🏏

Cricket analogy: A case statement matching against start|up) is like a captain's team-sheet logic branching on the toss result: 'bat|field)' triggers one strategy, cleaner than a long chain of if-elif checks for every possible toss outcome.

bash
#!/usr/bin/env bash
action="${1:-help}"

case "$action" in
    start|up)
        echo "Starting service..."
        ;;
    stop|down)
        echo "Stopping service..."
        ;;
    restart)
        echo "Restarting service..."
        ;;
    status)
        echo "Checking status..."
        ;;
    *)
        echo "Usage: $0 {start|stop|restart|status}" >&2
        exit 1
        ;;
esac
  • if branches on a command's exit status (0 = true), not on a native boolean type.
  • [[ ]] is a Bash keyword with safer parsing, regex support (=~), and no word-splitting on unquoted variables; [ ] is POSIX-portable but stricter and less forgiving.
  • Numeric comparisons use -eq, -lt, -gt, etc. in test brackets; (( )) arithmetic context allows familiar ==, <, > operators.
  • Always quote variables inside [ ] to avoid syntax errors when a variable is empty or contains spaces.
  • case statements match glob patterns against a single value and are cleaner than long if/elif chains for multi-way branching.
  • && and || provide concise guard-clause logic for single-action branching without a full if block.

Practice what you learned

Was this page helpful?

Topics covered

#Bash#LinuxShellScriptingStudyNotes#DevOps#ConditionalsInBashIfCase#Conditionals#Case#Elif#Else#StudyNotes#SkillVeris