Bash if Statements: Conditional Logic in Linux Shell Scripts
Learn Bash if, then, else, and elif statements with numeric comparisons, file tests, variables, examples, and troubleshooting tips.
What conditional execution means
Conditional execution means running a command block only when a tested condition succeeds or fails. A Bash if statement is a control structure that evaluates a condition and chooses which commands to run.
Bash determines whether a condition succeeded by examining the command's exit status. An exit status of 0 means success, so Bash treats the condition as true. A nonzero exit status means failure, so Bash treats the condition as false.
Conditional logic is useful when a script needs to validate user input, check whether a file exists, or select different program behavior based on a value.
This lesson assumes familiarity with the Bash shell, commands, variables, and basic Linux files and permissions.
Basic Bash if syntax
The standard multiline structure is:
if [ condition ]; then
commands_when_true
else
commands_when_false
fi
then begins the command block that runs when the condition succeeds. else begins the fallback block that runs when the condition fails. fi closes the entire if structure; it is if written backward.
then must be separated from the condition by either a newline or a semicolon. Both forms below are valid:
if [ -f "report.txt" ]
then
echo "A regular file was found"
fi
if [ -f "report.txt" ]; then
echo "A regular file was found"
fi
A complete one-line form is also valid when commands are separated correctly:
if [ -f "report.txt" ]; then echo "Found"; else echo "Missing"; fi
The test command and bracket syntax
[ condition ] is a command form of the Bash test command. The brackets are not merely punctuation built into the if statement. The opening bracket is the command name, and the closing bracket is an argument required by that command.
Spaces are required immediately inside the brackets. Write:
if [ "$age" -ge 21 ]; then
echo "Eligible"
fi
Do not write [$age -ge 21] or [ "$age" -ge 21] without the closing space. Without the required argument boundaries, Bash cannot parse the test correctly.
Bash also provides the alternative form [[ condition ]]. It is a Bash conditional construct with more shell-specific behavior and safer handling of several pattern and string operations. Use [ condition ] when learning the traditional test command form, and use [[ condition ]] when you specifically need its Bash features.
Variables and safe input handling
The read command accepts input and stores it in a shell variable. A variable reference such as $age undergoes variable expansion: Bash replaces the reference with the value stored in age.
read -r -p "How old are you? " age
echo "You entered: $age"
Quote variable expansions in tests unless you have a specific reason not to. Quoting keeps an empty value or a path containing spaces from being split into separate arguments.
if [ "$age" -ge 21 ]; then
echo "Eligible"
fi
An empty value or nonnumeric text can cause an integer expression error when used with numeric operators. Validate input before performing numeric comparisons when the value comes from a user or another untrusted source.
Numeric comparison operators
With the traditional [ ] test form, numeric comparisons use integer-specific operators. These are different from the string comparison operators used for text.
| Operator | Meaning | Example condition |
|---|---|---|
-eq | Equal to | [ "$count" -eq 3 ] |
-ne | Not equal to | [ "$count" -ne 0 ] |
-lt | Less than | [ "$age" -lt 18 ] |
-le | Less than or equal to | [ "$age" -le 21 ] |
-gt | Greater than | [ "$score" -gt 90 ] |
-ge | Greater than or equal to | [ "$age" -ge 21 ] |
These operators perform numeric comparison for integer values. Do not confuse them with string comparisons. For example, -eq is used for integer equality in a bracket test, while text comparisons use different syntax such as =.
Two-way branching with if and else
This script reads an age and prints one result for an eligible age and another for an ineligible age:
#!/bin/bash
read -r -p "How old are you? " age
if [ "$age" -ge 21 ]; then
echo "Eligible"
else
echo "Not eligible"
fi
The evaluation order is:
- Bash runs the test
[ "$age" -ge 21 ]. - If the test returns status
0, Bash runs thethenblock and skips theelseblock. - If the test returns a nonzero status, Bash skips the
thenblock and runs theelseblock. fiends the structure.
Multiple conditions with elif
elif means “else if.” It adds another test after an earlier if or elif condition fails. Bash checks branches from top to bottom, and the first successful branch runs. Remaining branches are skipped.
#!/bin/bash
read -r -p "How old are you? " age
if [ "$age" -le 21 ]; then
echo "Young range"
elif [ "$age" -ge 60 ]; then
echo "Older range"
else
echo "Middle range"
fi
Here, ages less than or equal to 21 match the first branch. If that test fails, Bash checks whether the age is greater than or equal to 60. The final else is the fallback for values that matched neither test, which produces the middle-range result.
Branch order matters. A broad condition placed before a narrower condition can make the later branch unreachable. Arrange boundaries deliberately and ensure ranges do not overlap unintentionally.
| Structure part | When it is evaluated or run | Outcome |
|---|---|---|
if condition | Evaluated first | Starts the selection process |
then block | Run when the preceding condition succeeds | Its commands execute and later tests are skipped |
elif condition | Evaluated only if earlier conditions failed | Provides another possible branch |
else block | Run when all preceding conditions fail | Provides the fallback behavior |
fi | Read after the selected block finishes | Ends the if structure |
File test operators
A file test operator checks a filesystem object's existence, type, or permissions. Quote a pathname variable, especially when it may contain spaces.
| Operator | What it tests | Example condition |
|---|---|---|
-e | The path exists | [ -e "$path" ] |
-f | The path exists and is a regular file | [ -f "$path" ] |
-d | The path exists and is a directory | [ -d "$path" ] |
-x | The path has executable permission | [ -x "$path" ] |
-w | The path has writable permission | [ -w "$path" ] |
-r | The path has readable permission | [ -r "$path" ] |
-e only asks whether a path exists. It does not say whether the path is a regular file or a directory. Use -f for a regular file and -d for a directory.
Check whether a path exists
#!/bin/bash
file="test_file"
if [ -e "$file" ]; then
echo "File exists: $file"
else
echo "File does not exist: $file"
fi
Check file type and access
#!/bin/bash
read -r -p "Enter a path: " path
if [ -d "$path" ]; then
echo "This path is a directory"
elif [ -f "$path" ]; then
echo "This path is a regular file"
if [ -r "$path" ] && [ -w "$path" ]; then
echo "It is readable and writable"
elif [ -r "$path" ]; then
echo "It is readable but not writable"
elif [ -w "$path" ]; then
echo "It is writable but not readable"
else
echo "It is neither readable nor writable"
fi
else
echo "The path is not a regular file or directory"
fi
The nested tests demonstrate that file type and access are separate questions. A path can exist without being a regular file, and a regular file can have different read, write, or execute permissions.
For more background, see how to determine file type and the Linux file structure.
Writing and running Bash scripts
A shebang is the first-line interpreter declaration in an executable script. For Bash, the usual declaration is #!/bin/bash.
#!/bin/bash
if [ -f "settings.conf" ]; then
echo "Configuration found"
else
echo "Configuration missing"
fi
Save the content in a file such as check.sh. Grant execute permission and run it from the current directory:
chmod +x check.sh
./check.sh
You can also invoke the script through Bash directly. This does not require changing the executable permission:
bash check.sh
Common syntax and logic pitfalls
- Missing spaces around brackets: Use
[ "$age" -ge 21 ], not["$age" -ge 21]. The spaces separate arguments to thetestcommand. - Missing separator before
then: On one line, writeif [ condition ]; then. Alternatively, putthenon the next line. - Omitted
fi: Everyifstructure needs a closingfi. Otherwise Bash may report an unexpected end-of-file error. - Unquoted expansions: Write
[ -e "$file" ], not[ -e $file ]. An empty value or a path containing spaces can change the arguments passed to the test command. - Incorrect branch ordering: Bash stops at the first matching branch. A broad earlier condition can prevent a later
eliffrom ever running. - Confusing string and numeric syntax: Use
-eq,-lt, and-gefor integer comparisons with[ ]. Text comparisons use different operators and should not be substituted casually. - Invalid numeric input: Empty or nonnumeric input can produce an integer expression error. Validate user input before using numeric operators.
Troubleshooting if statements
| Problem | Likely cause | Fix |
|---|---|---|
The shell reports an error near then. | The condition and then are on one line without a semicolon, or then is missing. | Use a newline before then or write if [ condition ]; then. |
| The test reports a missing bracket or unexpected operator. | Spaces were omitted after [ or before ]. | Write whitespace around the test expression, such as [ "$age" -ge 21 ]. |
| A numeric comparison produces an integer expression error. | The variable is empty or contains nonnumeric text. | Validate the input before applying numeric operators. |
| A file test fails for a path containing spaces. | The file variable was expanded without quotes. | Use [ -e "$file" ]. |
An elif branch never runs. | An earlier condition already matches the same values. | Reorder the tests and check for overlapping ranges. |
| The script ends with an unexpected end-of-file error. | The structure has no closing fi. | Add fi after the final branch. |
Practice checklist
- Write an
if/elsescript that tests whether a number is at least 21. - Extend it with
elifto classify younger, middle-range, and older input. - Store a pathname in a variable and test it with
-e,-f, and-d. - Add
-rand-wchecks for a regular file. - Run the script once with
bash script.shand once after usingchmod +x script.shand./script.sh.