Bash if Statements: Conditional Logic in Shell Scripts

Learn Bash if, elif, and else statements, test syntax, numeric comparisons, input validation, and file and directory checks.

A Bash if statement evaluates a condition and chooses which command block to run. This lets a script make decisions instead of always executing the same commands. Common uses include validating user input, checking whether files exist, controlling script flow, and responding to system state.

A condition is a conditional expression: a test whose result determines which branch runs. In shell scripting, commands communicate results through an exit status. Status 0 conventionally means success, or true. Any nonzero status means failure, or false. An if statement runs its successful branch when the condition command returns status 0.

Basic Bash if Syntax

The basic structure uses if, then, an optional else, and fi. The keyword fi closes the block; it is if spelled in reverse.

#!/bin/bash

if [ condition ]; then
  commands_when_true
else
  commands_when_false
fi

In this multiline form, then follows the test on the same line after a semicolon. It can also go on the next line:

if [ condition ]
then
  commands_when_true
else
  commands_when_false
fi

The semicolon is a command separator. If then remains on the same line as the test, write ; before it. Commands in only the selected branch run after Bash evaluates the condition.

Branching Keywords

Keyword — Role — Required or optional

if — Starts the first condition — Required

then — Starts commands for a successful condition — Required

elif — Adds another condition — Optional

else — Starts the fallback commands — Optional

fi — Terminates the entire if statement — Required

The test Command and Bracket Syntax

Bash provides the test command for evaluating file, string, and numeric conditions. The form [ ... ] is another command form of test; the brackets are not merely punctuation.

test "$age" -ge 18
[ "$age" -ge 18 ]

These two commands express the same test. Whitespace is mandatory after the opening bracket and before the closing bracket. The closing bracket is an argument required by the bracket form.

# Correct
[ "$file" -e ]

# Incorrect: missing spaces
["$file" -e]

# Correct file test
[ -e "$file" ]

Quote variable expansions in tests by default, especially when values can be empty or come from users. Quoting prevents spaces and shell metacharacters in a value from being interpreted as separate words or syntax.

Reading Input and Using Variables

A variable stores a named shell value. Assign without spaces around the equals sign, and expand the value with a leading dollar sign. The Bash builtin read collects input into a variable.

#!/bin/bash

printf "Enter your age: "
read -r age

if [ "$age" -ge 18 ]; then
  echo "Access allowed."
else
  echo "Access denied."
fi

Here, read -r age stores the response in age, and "$age" supplies that value to the numeric test.

Numeric Comparisons

Numeric operators compare integer values, not text. Use them only after ensuring that the input is a valid integer.

Operator — Meaning — Example condition

-eq — Equal to — [ "$age" -eq 18 ]

-ne — Not equal to — [ "$age" -ne 18 ]

-lt — Less than — [ "$age" -lt 18 ]

-le — Less than or equal to — [ "$age" -le 18 ]

-gt — Greater than — [ "$age" -gt 18 ]

-ge — Greater than or equal to — [ "$age" -ge 18 ]

Validate Before an Integer Test

An empty or non-numeric value can cause an integer-expression error. Check the input before using -ge, -le, or another numeric operator.

#!/bin/bash

printf "Enter your age: "
read -r age

if [[ ! "$age" =~ ^[0-9]+$ ]]; then
  echo "Please enter a whole number."
elif [ "$age" -ge 18 ]; then
  echo "Access allowed."
else
  echo "Access denied."
fi

The expression with [[ ... ]] checks that the value contains one or more digits. It rejects blank input and values containing letters or other characters before the bracket-form numeric test runs.

if, elif, and else Branches

Use elif to add mutually exclusive alternatives. Bash evaluates conditions from top to bottom and runs the first branch whose condition succeeds. Once a branch runs, later conditions are not evaluated. The else branch is the fallback when no preceding condition succeeds.

#!/bin/bash

printf "Enter your age: "
read -r age

if [[ ! "$age" =~ ^[0-9]+$ ]]; then
  echo "Invalid age. Enter a whole number."
elif [ "$age" -lt 13 ]; then
  echo "You are in the younger range."
elif [ "$age" -le 64 ]; then
  echo "You are in the middle range."
else
  echo "You are in the older range."
fi

The order matters. An age of 10 matches the first numeric range and stops there. An age of 40 fails the first range, succeeds at -le 64, and never reaches else. Design overlapping ranges carefully so their boundaries produce the intended result.

File and Directory Conditionals

File tests evaluate a pathname. Store the pathname in a variable and quote its expansion so paths containing spaces remain one argument.

Operator — What it tests — Typical use

-e — The path exists — Check for any existing filesystem path

-f — The path is a regular file — Confirm a normal file before reading it

-d — The path is a directory — Confirm a directory before entering it

-x — The path has execute permission — Check whether it can be executed

-w — The path is writable — Check whether it can be written

-r — The path is readable — Check whether it can be read

Checking Whether a Path Exists

#!/bin/bash

file="/tmp/report.txt"

if [ -e "$file" ]; then
  echo "The path exists: $file"
else
  echo "The path does not exist: $file"
fi

-e answers whether a path exists. It does not say whether the path is a regular file or a directory. Use -f and -d when the type matters.

#!/bin/bash

path="project data"

if [ -f "$path" ]; then
  echo "This is a regular file."
elif [ -d "$path" ]; then
  echo "This is a directory."
else
  echo "The path is missing or another type."
fi

Checking Permissions

#!/bin/bash

file="script.sh"

if [ -r "$file" ]; then
  echo "The path is readable."
fi

if [ -w "$file" ]; then
  echo "The path is writable."
fi

if [ -x "$file" ]; then
  echo "The path has execute permission."
fi

Each if is independent, so more than one permission message can be printed. A test describes the permissions available to the current process; it is not a guarantee that a later operation will succeed under every system condition.

Complete Script Examples

Minimum-Age Access Check

#!/bin/bash

printf "Enter your age: "
read -r age

if [[ ! "$age" =~ ^[0-9]+$ ]]; then
  echo "Invalid input. Enter a whole number."
elif [ "$age" -ge 21 ]; then
  echo "You meet the minimum age."
else
  echo "You are below the minimum age."
fi

Save a script as age-check.sh. Run it with bash age-check.sh, or make it executable and run it directly:

chmod +x age-check.sh
./age-check.sh

File Existence and Type Check

#!/bin/bash

printf "Enter a path: "
read -r file

if [ -e "$file" ]; then
  if [ -f "$file" ]; then
    echo "The path exists and is a regular file."
  elif [ -d "$file" ]; then
    echo "The path exists and is a directory."
  else
    echo "The path exists but is another type."
  fi
else
  echo "The path does not exist."
fi

Correctness Rules and Troubleshooting

  • Missing bracket spaces: Write [ condition ], not [condition]. The shell reports a malformed test or a missing ] when the required whitespace is absent.
  • Syntax error near then: Put then on its own line, or use if [ condition ]; then. Also check that the test is correctly closed.
  • Integer expression error: Validate blank and non-numeric input before applying numeric operators. Quote expansions such as "$age".
  • Incorrect path result: Quote path variables, for example [ -e "$file" ], especially when a pathname can contain spaces.
  • Wrong elif branch: Bash selects the first successful condition. Reorder overlapping ranges and make boundary operators explicit.
  • Script fails before testing conditions: Use a valid shebang, such as #!/bin/bash, and run it with bash script.sh or execute it after chmod +x script.sh.
  • Spelling and spacing: Shell keywords, test operators, brackets, and delimiters must be spelled exactly and separated as required.

Logical Next Steps

After basic branches work, extend conditions with compound logic. The && operator represents logical AND, || represents logical OR, and ! negates a condition.

if [ -f "$file" ] && [ -r "$file" ]; then
  echo "A readable regular file was found."
fi

if [ -z "$name" ] || [ "$name" = "quit" ]; then
  echo "No usable name was supplied."
fi

For selecting among many fixed patterns or values, investigate Bash case statements. Also test scripts with expected inputs and unexpected inputs: blank values, non-numeric values, paths with spaces, missing paths, and boundary numbers.

Summary

  • An if statement evaluates a command or test and runs the selected command block.
  • test condition and [ condition ] are equivalent forms; bracket spacing is required.
  • Use -eq, -ne, -lt, -le, -gt, and -ge for integer comparisons.
  • Validate user input before numeric comparisons.
  • Use elif for ordered alternatives and else as the fallback.
  • Use -e, -f, and -d to distinguish existence, regular files, and directories; use -r, -w, and -x for permissions.
  • Start Bash scripts with #!/bin/bash and test both normal and unexpected inputs.