Branches in Bash often execute the wrong path when string comparisons use the numeric operator -eq or when variables are left unquoted. Switching to Bash’s [[ ]] test with the correct operators fixes those logic errors and makes scripts easier to maintain.
Method 1: Write if…else with Bash [[ ]] (strings, numbers, and files)
[[ ]]. This form avoids accidental glob expansion and word splitting, which reduces surprising behavior compared to single brackets.#!/usr/bin/env bash
set -euo pipefail
read -r -p "Enter a value: " val
if [[ "$val" == "admin" ]]; then
echo "Welcome, admin."
else
echo "Access limited."
fi
== or !=. Quote variables to keep empty values and spaces from breaking your tests. This prevents unintended matches caused by unquoted expansions.user="alice"
target="alice"
if [[ "$user" == "$target" ]]; then
echo "Usernames match."
else
echo "Usernames differ."
fi
[[ ]]: -eq, -ne, -gt, -ge, -lt, -le. These operate on numbers only and avoid lexicographic comparisons.read -r -p "Enter a number: " n
if [[ "$n" -gt 10 ]]; then
echo "Greater than 10."
elif [[ "$n" -eq 10 ]]; then
echo "Equal to 10."
else
echo "Less than 10."
fi
-f path: regular file exists.-d path: directory exists.-e path: path exists (any type).-x path: executable file exists.-r path/-w path: readable / writable.
path="./data.txt"
if [[ -f "$path" ]]; then
echo "File exists."
else
echo "Creating file..."
: > "$path"
fi
&& (AND), || (OR), and ! (NOT). Group them with parentheses for clarity and correct precedence.file="./report.log"
size=5
# True when the file exists AND size is at least 5
if [[ -f "$file" ]] && [[ "$size" -ge 5 ]]; then
echo "Process report."
fi
# True when user is admin OR staff
role="staff"
if [[ "$role" == "admin" || "$role" == "staff" ]]; then
echo "Privileged."
fi
# Negation: path exists but is not a directory
if [[ -e "$file" && ! -d "$file" ]]; then
echo "Path is not a directory."
fi
elif to short-circuit checks. Once one condition is true, later branches are skipped, which speeds up evaluation and reduces unnecessary commands.status="warning"
if [[ "$status" == "error" ]]; then
echo "Exit immediately."
exit 1
elif [[ "$status" == "warning" ]]; then
echo "Log and continue."
else
echo "All good."
fi
Join readers who trust AllThings.How
Add us as a preferred source on Google so our practical guides show up first next time you search.
Add to Google Preferences →Method 2: Compare integers with (( )) arithmetic evaluation
(( )), you can write math-like comparisons (<, >, ==) without quoting variables. This is concise and fast for integer-only checks.a=12 b=8
if (( a > b )); then
echo "a is larger."
elif (( a == b )); then
echo "Equal."
else
echo "b is larger."
fi
x=7 y=3 z=10
if (( (x > y) && (z >= 10) )); then
echo "Threshold met."
fi
[[ ]] before evaluating.read -r n
if [[ "$n" =~ ^-?[0-9]+$ ]] && (( n >= 0 )); then
echo "Non-negative integer."
else
echo "Invalid number."
fi
Method 3: Use POSIX-portable [ ] for broader shell compatibility
/bin/sh). Remember that [ is a command: spaces and quoting are required.#!/bin/sh
a="hello"
b="hello"
if [ "$a" = "$b" ]; then
echo "Match."
else
echo "No match."
fi
a=5
b=30
if [ "$a" -lt "$b" ]; then
echo "a is less than b."
fi
< or >, escape the operators to prevent redirection. Many errors stem from forgetting this.x="apple" y="banana"
if [ "$x" \< "$y" ]; then
echo "apple comes before banana."
fi
file="./cfg.ini"
if [ -r "$file" ] && [ -w "$file" ]; then
echo "Config is readable and writable."
fi
Method 4: Replace long chains with case for value-based branching
case to simplify multiple equality checks on a single variable. This reduces nested if blocks and improves readability and speed when matching patterns.read -r -p "Enter mode (start|stop|status): " mode
case "$mode" in
start) echo "Starting...";;
stop) echo "Stopping...";;
status) echo "Service is running.";;
*) echo "Unknown mode."; exit 1;;
esac
level="warn"
case "$level" in
error|err) echo "Exit with failure."; exit 1;;
warn|warning) echo "Log warning.";;
info|debug) echo "Proceed normally.";;
*) echo "Unrecognized level.";;
esac
Method 5: Test multiple conditions correctly and debug failures
if [[ "$Z" == "TRUE" || ( "$X" == "TRUE" && "$Y" == "TRUE" ) ]]. Parentheses clarify precedence and prevent subtle bugs.X="TRUE" Y="FALSE" Z="TRUE"
if [[ "$Z" == "TRUE" || ( "$X" == "TRUE" && "$Y" == "TRUE" ) ]]; then
echo "Conditions met."
else
echo "Conditions not met."
fi
== for strings and -eq for integers. Mixing them causes incorrect branches (e.g., string compared with -eq always fails).#!/usr/bin/env bash
set -x # turn on trace
# ... your conditionals here ...
set +x # turn off trace
then/else, leave spaces around brackets, and favor early exits on error branches to reduce nesting.Reference: Common test operators at a glance
This quick list helps you pick the right operator for the job.
- Strings:
==,!=,-n var(length > 0),-z var(empty). - Integers:
-eq,-ne,-gt,-ge,-lt,-le; or use(( ))with<,>,==. - Files:
-f(regular),-d(directory),-e(exists),-x(executable),-r(readable),-w(writable). - Logic:
&&(AND),||(OR),!(NOT); group with( ... )inside[[ ]].
With the right test form, correct operators, and consistent quoting, your Bash conditionals execute the intended branch every time. Keep [[ ]] for Bash scripts, use (( )) for math, and fall back to [ ] only when portability is a must.






