In Bash scripting, being able to make decisions is fundamental. You often need your script to perform different actions based on whether a file exists, a variable has a certain value, or a command succeeded. This is where if-else statements and conditionals come in. They are the core of logical control flow in your scripts.
This guide will walk you through everything you need to know, from the basic concept of an exit status to writing complex, multi-layered conditions.
The Foundation: Understanding Exit Status
Before we write our first if statement, we need to understand how Bash decides if something is “true” or “false”. Every command you run in Linux finishes with an exit status (also called a return code).
0means the command was successful.- Any number other than
0(from 1 to 255) means the command failed.
You can see the exit status of the last command by checking the special variable $?.
Let’s try it. First, a successful ls command:
ls /etc/hosts
echo $?
The output of the echo command will be 0. Now, let’s try a command that fails:
ls /etc/non_existent_file
echo $?
This time, ls prints an error, and echo $? will show a non-zero number (like 2), indicating failure. The if statement is designed to act on this exact principle.
Step 1: The Basic if Statement
The simplest conditional checks if a command’s exit status is 0. If it is, the code inside the then block is executed.
The syntax is:
if [ condition ]; then ... fi
Let’s break this down:
if: The keyword that starts the conditional block.[ condition ]: This is a shortcut for thetestcommand. It evaluates theconditionand returns an exit status of0(true) or1(false). Crucially, you must have spaces around the brackets and around the operators inside them!then: The keyword that starts the code block to be executed if the condition is true.fi: Theifstatement spelled backward, which closes the block.
Example: Check if a file exists
The -f operator tests if a given path exists and is a regular file.
#!/bin/bash
FILENAME="my_document.txt"
if [ -f "$FILENAME" ]; then
echo "$FILENAME exists."
fi
echo "Script finished."
If my_document.txt exists in the same directory, the script will print that it exists. If not, it will do nothing and just print “Script finished.”
Step 2: Adding an else Block
What if you want to do something when the condition is not met? That’s what the else block is for.
The syntax is:
if [ condition ]; then ... else ... fi
Example: Check for a directory and create it if it doesn’t exist
The -d operator tests if a path is a directory.
#!/bin/bash
DATA_DIR="/var/log/my_app"
if [ -d "$DATA_DIR" ]; then
echo "Directory $DATA_DIR already exists."
else
echo "Directory $DATA_DIR not found. Creating it now..."
mkdir -p "$DATA_DIR"
fi
This is a very common and practical use case for if-else in system administration scripts.
Step 3: Chaining Conditions with elif
Sometimes you need to check more than one condition. You can chain multiple checks together using elif (short for “else if”).
The syntax is:
if [ condition1 ]; then ... elif [ condition2 ]; then ... else ... fi
Example: Greet a user based on their username
#!/bin/bash
# The $USER variable automatically holds the current user's name
if [ "$USER" == "root" ]; then
echo "Welcome, Administrator. You have ultimate power."
elif [ "$USER" == "guest" ]; then
echo "Welcome, Guest. Your access is limited."
else
echo "Welcome, $USER."
fi
The script checks conditions from top to bottom. As soon as it finds a true condition, it executes that block and skips the rest of the if/elif/else structure.
Step 4: Common Conditional Operators
The test command ([ ... ]) provides a rich set of operators for different kinds of comparisons.
File Operators
| Operator | Description |
|---|---|
-e file | True if file exists. |
-f file | True if file exists and is a regular file. |
-d dir | True if dir exists and is a directory. |
-s file | True if file exists and is not empty. |
-r file | True if file is readable by you. |
-w file | True if file is writable by you. |
-x file | True if file is executable by you. |
String Operators
| Operator | Description |
|---|---|
str1 = str2 | True if str1 is identical to str2. Use == for better readability. |
str1 != str2 | True if the strings are not identical. |
-z str | True if the string str is empty (zero length). |
-n str | True if the string str is not empty. |
Pro Tip: Always quote your variables (e.g.,
"$VAR") inside test conditions to prevent errors if the variable is empty or contains spaces.
Integer Operators
| Operator | Description |
|---|---|
int1 -eq int2 | Equal |
int1 -ne int2 | Not Equal |
int1 -gt int2 | Greater Than |
int1 -ge int2 | Greater Than or Equal To |
int1 -lt int2 | Less Than |
int1 -le int2 | Less Than or Equal To |
Step 5: The Modern Approach: Double Brackets [[ ... ]]
While single brackets [ ] are POSIX-compliant and work everywhere, Bash offers an enhanced version: double brackets [[ ]]. They are generally safer and more powerful.
Key Advantages of [[ ... ]]:
- No word splitting: You don’t strictly need to quote variables, as they won’t be split by spaces. (It’s still good practice, though!)
- C-style logical operators: You can use
&&for AND and||for OR. - Pattern matching: Supports
==and!=with shell globbing (e.g.,[[ $file == *.txt ]]). - Regex matching: Use the
=~operator for regular expression matching.
Example: Using && and Regex
This script checks if a variable contains only numbers.
#!/bin/bash
INPUT="12345"
if [[ "$INPUT" =~ ^[0-9]+$ && ${#INPUT} -gt 3 ]]; then
echo "'$INPUT' is a number and is longer than 3 digits."
else
echo "'$INPUT' is not a valid ID."
fi
Here, [[ "$INPUT" =~ ^[0-9]+$ ]] checks if the input matches the regex for one or more digits, and && ${#INPUT} -gt 3 checks if its length is greater than 3. This kind of complex check is much cleaner with double brackets.
Conclusion
You’ve now learned the essentials of conditional logic in Bash. By understanding exit status and mastering the if-elif-else-fi structure, you can make your scripts smarter, more robust, and capable of handling a wide variety of situations.
Key Takeaways:
- Every command has an exit status:
0for success, non-zero for failure. - The basic structure is
if [ condition ]; then ... fi. - Use
elsefor a fallback action andeliffor multiple checks. - Always quote your variables inside conditionals, especially when using single brackets
[ ]. - For modern Bash scripting, prefer the more powerful and safer double brackets
[[ ]].
As a next step, consider learning about case statements, which can be a cleaner alternative to long if/elif chains when you’re checking a single variable against multiple possible values. Happy scripting