#

Bash

Subscribe
Bash October 29, 2026·8 min read

Bash if, elif, and else Statements

In a Bash script, use if to act on a command's exit status, else for a fallback, and elif to pick one of several exclusive outcomes. When more than one action needs to run, use separate if statements. If the script must also work under POSIX sh, choose test or [ ... ] over the Bash-only [[ ... ]] or (( ... )), then check the syntax and exercise both outcomes with test inputs.

How Bash if decides which branch runs

Bash exit status

Bash runs the command after if and checks its exit status. Status 0 means success, so Bash enters then. A nonzero status moves Bash on to an alternative branch.

if true; then
    printf '%s\n' 'The command succeeded'
else
    printf '%s\n' 'The command failed'
fi

This prints The command succeeded. Change true to false and the else branch prints The command failed.

true and false are commands, and if checks a command's status directly, so square brackets are optional (see the GNU Bash Reference Manual).

Bash arithmetic conditions

(( ... )) returns success when the expression's value is nonzero and failure when it is zero.

if (( 0 )); then
    printf '%s\n' 'first branch'
else
    printf '%s\n' 'second branch'
fi

This prints second branch. Replace 0 with 3 and it prints first branch.

Write if, else, and elif statements in Bash

Bash branch structure

Use if for an action that needs a condition, else when you need a fallback, and elif to try several alternatives in order.

GoalStructureWhat runs
One conditional actionif ...; then ...; fiThe body only if its test succeeds
Two outcomesif ...; then ...; else ...; fiOne of the two bodies
Several exclusive outcomesif ...; elif ...; else ...; fiThe first successful branch
Independent actionsSeparate if statementsMore than one body can run

An else clause is optional. If no condition succeeds and there's no else, the shell continues after fi.

mode='preview'

if [[ $mode == 'prod' ]]; then
    printf '%s\n' 'Production'
elif [[ $mode == 'preview' ]]; then
    printf '%s\n' 'Preview'
else
    printf '%s\n' 'Unknown mode'
fi

This prints Preview. After the successful elif, Bash skips the remaining branches, so in one chain at most one branch body runs.

Independent Bash conditions

Separate if statements are evaluated independently:

if true; then
    printf '%s\n' 'First action'
fi

if true; then
    printf '%s\n' 'Second action'
fi

Both lines print. Use this form when the same input can legitimately trigger more than one action. A case statement is simpler than a long elif chain when you're matching against many literal or pattern-based choices.

Check strings, numbers, and files inside a condition

Bash comparison types

A regular file, a directory, a string, and an integer each need a different test.

Use -f for a regular file and -d for a directory. In a single-bracket test, quote a filename variable so a name with spaces stays one argument:

file='draft notes.txt'

if [ -f "$file" ]; then
    printf '%s\n' 'Regular file found'
else
    printf '%s\n' 'Regular file missing'
fi

This prints Regular file found if draft notes.txt exists as a regular file and Regular file missing if it doesn't.

Bash string comparison

String equality compares against a specific value:

name='admin'

if [[ $name == 'admin' ]]; then
    printf '%s\n' 'Matching name'
fi

To test whether a string has content, use -n (nonempty) or -z (empty).

For a known integer, Bash arithmetic syntax reads cleanly:

count=3

if (( count > 0 )); then
    printf '%s\n' 'Positive count'
fi

With test or [ ... ], write integer greater-than as -gt:

count=3

if [ "$count" -gt 0 ]; then
    printf '%s\n' 'Positive count'
fi
Numeric input checks

Those examples hard-code the integer. If a value comes from a prompt or another untrusted source, confirm it's a valid number before passing it to an arithmetic comparison. A word like index can be a legitimate command, so run a string test on it before any numeric-range test.

Use a command, [ ], [[ ]], or (( ))?

Bash conditional syntax
FormUseful forCompatibilityPoint to remember
if command; thenCommand successShell control-flow syntaxThe command may perform actions
if [ ... ]; thenFile, string, integer testsPOSIX-styleSpaces and expansion quoting matter
if [[ ... ]]; thenBash strings, files, patternsBash-specificDifferent expansion and pattern rules
if (( ... )); thenInteger arithmeticBash-specificZero arithmetic value means unsuccessful status

[ is a command related to test, and its closing ] must be a separate argument. Double brackets are a Bash conditional construct, so ordinary word splitting inside them behaves differently. Three easy mistakes to check first are treating a command's printed text as its status, mixing single- and double-bracket syntax, and using a numeric comparison for strings.

For more on quoting habits, see the r/bash quoting discussion.

Bash pattern matching

In [[ ... ]], quote the right-hand operand when you want literal equality. Leave a pattern unquoted when you want a pattern match:

filename='notes.log'

[[ $filename == *.log ]]
[[ $filename == '*.log' ]]

The first test succeeds because *.log matches notes.log. The second compares against the literal characters *.log and fails.

Bash-only forms like [[ ... ]] and (( ... )) need a compatible interpreter. Run a [[ ]] test under dash and you get [[: not found.

Why a Bash if condition is always true or false

Bash condition debugging

A wrong branch means the test received different operands than you intended.

An r/bash user wrote, “This always seems to execute regardless of how I format or change it. What am I doing wrong?” In the r/bash conditional discussion, the problem was missing spaces around the comparison.

answer='no'

if [[ "$answer"=="yes" ]]; then
    printf '%s\n' 'Matched'
fi

Without spaces around ==, Bash reads the whole expression as one nonempty string, so the test passes and Matched prints. With [[ "$answer" == "yes" ]], Bash recognizes the equality comparison and evaluates it false for no.

SymptomCauseRepair
Equality test always succeeds[[ "$answer"=="yes" ]][[ "$answer" == "yes" ]]
Directory check falls throughWrong or unset path variableUse the variable containing the intended path
Nonempty test succeeds on an empty value[ -n $value ][ -n "$value" ]
Empty variable test

For value='', the unquoted form becomes [ -n ]. That command treats -n as a nonempty string and succeeds. Quoting the expansion preserves the empty argument and gives the intended false result.

value=''
printf 'value=<%s>\n' "$value"

if [ -n "$value" ]; then
    printf '%s\n' 'Nonempty'
else
    printf '%s\n' 'Empty'
fi

This prints value=<> followed by Empty. A directory test with an unset variable needs a different repair: check the value you use as the path.

When short-circuit commands behave differently from if/else

Bash shortcut behavior

The command list A && B || C runs C whenever B fails, even when A succeeded.

true && false || printf '%s\n' 'fallback'

This prints fallback. true succeeded, so Bash ran false. That failed, so the || side ran.

if true; then
    false
else
    printf '%s\n' 'fallback'
fi

Here the else body never runs. The test after if succeeded, so Bash had already chosen then, and the failing false inside it changes nothing.

Conditional fallback comparison

If C does something you don't want after B fails, write an explicit if/else. Take extra care when C modifies or deletes files. ShellCheck's SC2015 warning describes the same control-flow trap.

A beginner in r/bash asked, “I am wondering why commands after the if statement won't execute ?” The r/bash process-control discussion concerned an exec adduser command inside a conditional.

exec replaces the running shell process, so commands after it in the original shell never run.

Fix unexpected else, then, and fi errors

Bash syntax errors
if true; then
    if false; then
        printf '%s\n' 'Inner branch'
    fi
fi

Every if needs its own fi. Here the indented fi closes the inner test and the last fi closes the outer one.

  • A loop closes with done, so a parser error pointing at done can mean an earlier fi is missing.
  • In compact form, put a newline or semicolon before then, as in if true; then printf '%s\n' 'ok'; fi.
  • Use elif for another branch in the same chain. An else followed by another if opens a new conditional and needs its own fi.

Check a Bash if statement before using it

Bash script testing

Check the syntax of a copy of the script first, then run test inputs that select each branch.

mode=${1-}

if [[ $mode == ready ]]; then
    printf '%s\n' 'Ready'
else
    printf '%s\n' 'Other'
fi

Save this script as check-mode.sh and check its syntax:

bash -n check-mode.sh

The -n option reads the script's syntax without running its commands. It catches errors like unclosed constructs, but it can't tell you whether a valid condition selects the intended branch.

Run these cases:

bash check-mode.sh ready
bash check-mode.sh pending
bash check-mode.sh ''
InputSelected branchPrinted result
readythenReady
pendingelseOther
Empty stringelseOther
Branch boundary verification

Strict inequalities need boundary values. With count=100, if (( count > 100 )) is false, so the else body runs, and that branch covers 100 as well as smaller numbers. Changing the operator to >= makes the same value select then.

If ShellCheck is installed, shellcheck check-mode.sh flags common shell pitfalls such as unquoted expansions. For a stubborn case, bash -x check-mode.sh ready prints each command as it runs. Run tracing only with safe inputs in a controlled setting, because it can reveal sensitive values and run real side effects.

NoScript Team