How to Use Conditional Statements in Bash: The If-Else Fi Basics

August 11, 2023

Conditional Statements in bash help you make decision. For example, you can tell your script to run this code if certain conditions are met and do that if some other conditions are met. It is one of the most basic concept in bash (and in programming).

In this article, I will talk about basics of How to use Conditional Statements in Bash - if, if-else, if-elif-else, case, and various other types of conditional expressions like -gt, -lt, <, >, -nt (file1 is newer than file2), etc.

The if Statement

The if statement is the simplest form of a conditional statement in Bash. It executes a code only if a specific condition evaluates to true.

Syntax:

if [[ condition ]]; then
    # execute the code if the condition is true
fi

Example:

if [[ 7 -eq 7 ]]; then
    echo "Number is 7"
fi

Output:

Number is 7

In this example, the condition 7 -eq 7 evaluates to true, so the code block inside the if statement is executed.

The if-else Statement

The if-else statement extends the if statement. It executes different code blocks based on whether the condition is true or false.

Syntax:

if [[ condition ]]; then
    # if-code to execute if the 'condition' is true
else
    # else-code to execute if the 'condition' is false
fi

Example:

if [[ 5 -gt 10 ]]; then
    echo "Number is greater than 10"
else
    echo "Number is not greater than 10"
fi

Output:

Number is not greater than 10

Explanation:

Since the condition 5 -gt 10 is false, the code block inside the else branch is executed.

The if-elif-else Statement

The if-elif-else statement allows you to test multiple conditions in sequence and execute different code blocks based on the first true condition encountered.

Syntax:

if [[ condition1 ]]; then
    # Code to execute if condition1 is true
elif [[ condition2 ]]; then
    # Code to execute if condition2 is true
else
    # Code to execute if no conditions are true
fi

Example:

if [[ 18 -lt 18 ]]; then
    echo "You are a minor"
elif [[ 18 -eq 18 ]]; then
    echo "You just turned 18"
else
    echo "You are an adult"
fi

Output:

You just turned 18

In this case, the first condition (18 -lt 18) is false, but the second condition (18 -eq 18) is true, so the code block corresponding to the elif branch is executed.

-gt: greater than
-ge: greater than or equal to
-lt: less than
-le: less than or equal to

The case Statement

The case statement provides a way to test multiple conditions against a single value. It is particularly useful when you want to compare a variable against multiple possible values. Here's the basic structure:

case "$variable" in
    "value1")
        # Code1 to execute for value1
        ;;
    "value2")
        # Code2 to execute for value2
        ;;
    *)
        # Code3 to execute for all other cases
        ;;
esac

Let's consider an example:

fruit="apple"
case $fruit in
    "apple")
        echo "It's an apple";;
    "banana")
        echo "It's a banana";;
    *)
        echo "Unknown fruit";;
esac

Output:

It's an apple

In this case, since the value of the fruit variable is "apple", the corresponding code block is executed.

Note: You need to use * not "*" since * is not a string here.

Numeric Comparison

You can use numeric comparisons to check relationships between numbers. For example:

if [[ 15 -lt 20 ]]; then
    echo "15 is less than 20"
fi

Output:

15 is less than 20

In this example, the condition 15 -lt 20 is true, so the code block is executed.

The [ Command (aka test)

The [ command and test are basically the same. For example, test -f <file_name> and [ -f <file_name>] are the same.

The [ command is a program that evaluates conditional expressions (-n, -z, -r, etc. given below). It is commonly used in conditional statements. For examples see the headings given below.

However, please note that for more advanced conditional tests, it would be more beneficial to use [[]] and (()). In my opinion, stick with double brackets because they work most of the time.

-f and -d for 'file' or 'directory' checks

You can use the -f and -d flags with the [ command to check if a file exists (-f) or if a directory exists (-d). For instance:

[ajay@legion bash]$ ls -1
bash_articles.md
bash_brace.md
bash_conditionals.md
bash_loops_article.md
bash_loops.md
bash_parameter_substitution.md
temp.md
testdir
if [ -f "bash_brace.md" ]; then
    echo "File exists: bash_brace.md"
fi

if [ -d "testdir" ]; then
    echo "Directory exists: testdir"
fi

Output:

File exists: bash_brace.md
Directory exists: testdir

In the first example, the -f flag checks if the file "bash_brace.md" exists, while in the second example, the -d flag checks for the existence of the directory "testdir".

String Comparison

String comparisons allow you to compare two strings for equality or inequality. For example:

if [ "smart" = "tech101" ]; then
    echo "Strings are equal"
else
    echo "Strings are not equal"
fi

Output:

Strings are not equal

In this case, the strings "smart" and "tech101" are not equal, so the code block in the else branch is executed.

Check if a File Exists and Is Readable

You can use the -r flag with the [[ command to check if a file exists and is readable:

[ajay@legion bash]$ ls -1
bash_articles.md
bash_brace.md
bash_conditionals.md
bash_loops_article.md
bash_loops.md
bash_parameter_substitution.md
temp.md
testdir
if [[ -r "bash_brace.md" ]]; then
    echo "File exists and is readable"
fi

Output:

File exists and is readable

The -r flag checks if the file "bash_brace.md" exists and has read permission.

Check if a Directory Exists and Is Writable

You can combine the -d and -w flags to check if a directory exists and is writable:

$ ls -al        
total 96
drwxr-xr-x  3 ajay ajay  4096 Aug  8 14:05 .
drwxr-xr-x 28 ajay ajay 16384 Aug  9 09:24 ..
-rw-r--r-- 1 ajay ajay 11024 Aug  6 15:31 bash_articles.md
-rw-r--r-- 1 ajay ajay  7087 Aug  3 14:19 bash_brace.md
drwxr-xr-x  2 ajay ajay  4096 Aug  8 14:05 testdir
if [[ -d "testdir" && -w "testdir" ]]; then
    echo "Directory exists and is writable"
fi

Output:

Directory exists and is writable

In this example, the script checks if both the directory "testdir" exists (-d flag) and has write permission (-w flag).

Check if a String Is Empty or non-empty

Use -z and -n. Both are exactly opposite of each other.

Just remember the following to avoid the confusion:
-z means "null"
-n means "not null"

foo="bar"
if [[ -n "$foo" ]]; then
    echo "foo is not null"
fi

Output:

foo is not null
foo="bar"
if [[ -z "$foo" ]]; then
    echo "foo is null"
fi

Output:

foo=""
if [[ -n "$foo" ]]; then
    echo "foo is not null"
fi

Output:

foo=""
if [[ -z "$foo" ]]; then
    echo "foo is null"
fi

Output:

foo is null

Compare Strings for Equality

You can compare two strings for equality using the == operator:

str1="smarttech101"
str2="smarttech101"
if [[ "$str1" == "$str2" ]]; then
    echo "Strings are equal"
fi

Output:

Strings are equal

In this example, the string variables "str1" and "str2" give equal strings, so the code block is executed.

For variable expansion in bash, look over here

Check if a Variable Is Numeric

You can use regular expressions to check if a variable contains only numeric characters:

var="101"
if [[ "$var" =~ ^[0-9]+$ ]]; then
    echo "Variable is numeric"
fi

Output:

Variable is numeric

The regular expression ^[0-9]+$ matches strings that consist of one or more digits.

To learn about regular expression, look at this article

Check if a Command Succeeded

You can use the exit status of a command to determine if it succeeded. If the exit status is zero, the command succeeded:

if command; then
    echo "Command succeeded"
fi

Output (assuming command succeeds):

Command succeeded

In this case, the command is executed, and if it exits with a status of zero, the code block is executed.

Example:

filename="$HOME/Pictures/example.png"

if wget -qO "$filename" "https://smarttech101.com/test.png"
then
  notify-send "Image Downloaded"
else
  notify-send "Image Download Failed"
fi

If wget is unable to download the image, it will fail and notification is popped-up in your Linux machine.

Check if a File Is Executable

You can use the -x flag to check if a file is executable:

[ajay@legion .my_scripts]$ ls -al test.sh
-rwxr--r-- 1 ajay ajay 315 Aug  4 18:31 test.sh
if [[ -x "test.sh" ]]; then
    echo "File is executable"
fi

Output:

File is executable

The -x flag tests if the file "test.sh" has execute permission.

Compare Numeric Values

You can use arithmetic evaluation to compare numeric values using operators like <, >, <=, >=, ==, and !=. For example:

if (( 5 > 3 )); then
    echo "5 is greater than 3"
fi

Output:

5 is greater than 3

In this example, the arithmetic expression 5 > 3 is true, so the code block is executed.

Similarly, <.

Nested Conditionals

You can nest conditional statements within each other to create more complex decision structures. For example:

var=10
if [[ $var -gt 5 ]]; then
    if [[ $var -lt 100 ]]; then
        echo "$var is in between 5 and 100"
    fi
fi

Output:

10 is in between 5 and 100

In this example, both -gt 5 and -lt 100 are true, so the nested code block is executed.

Advanced File Checks

-ef checks

Bash provides additional checks to examine files in detail. You can use the -ef flag to check two files have same device and inode numbers:

file1="file.txt"
file2="link_to_file.txt"

touch "$file1"
ln -s "$file1" "$file2"

if [[ "$file1" -ef "$file2" ]]; then
    echo "file1 and file2 with same device and inode numbers"
fi

Output:

file1 and file2 with same device and inode numbers

In this example, both file1 and file2 point to the same content, so the -ef check succeeds.

-nt and -lt checks for files' date comparion

You can also compare file modification times using the -nt (newer than) and -ot (older than) flags:

file1="file1.txt"
file2="file2.txt"

touch "$file1"
sleep 2
touch "$file2"

if [[ "$file2" -nt "$file1" ]]; then
    echo "file2 is newer than file1"
fi

Output:

file2 is newer than file1

In this example, file2 was touched more recently than file1, so the -nt check returns true.

Conversely, you can use the -ot flag to check if a file is older than another:

file1="file1.txt"
file2="file2.txt"

touch "$file1"
sleep 2
touch "$file2"

if [[ "$file1" -ot "$file2" ]]; then
    echo "file1 is older than file2"
fi

Output:

file1 is older than file2

In this scenario, file1 was created earlier than file2, making the -ot check true.

Check the status of Shell Options and Variables

You can also use conditional statements to check the status of shell options and variables. For instance, you can check if a shell option is enabled:

optionname="emacs"

if [[ -o "$optionname" ]]; then
    echo "Shell option '$optionname' is enabled"
fi

Output:

Shell option 'emacs' is enabled

By default, my bash shell have enabled emacs mode, so testing above option turns out to be true and I get the given output. For optionname="vi", no output is generated, because I have not enabled vi mode.

Similarly, you can check if a shell variable is set:

myvar="Hello"

if [[ -v "myvar" ]]; then
    echo "Shell variable 'myvar' is set"
fi

Output:

Shell variable 'myvar' is set

In this example, the script verifies if the shell variable myvar is set.

You can also determine if a shell variable is a name reference:

declare -n refvar=myvar

if [[ -R "refvar" ]]; then
    echo "Shell variable 'refvar' is a name reference"
fi

Output:

Shell variable 'refvar' is a name reference

In this example, the script checks if the variable refvar is a name reference to another variable.

String Equality and Inequality

You can compare strings for equality using the

= operator, or for inequality using !=:

string1="smarttech101"
string2="smarttech101"

if [[ "$string1" == "$string2" ]]; then
    echo "Strings are equal"
fi

Output:

Strings are equal

In this case, the strings smarttech101 and smarttech101 are equal, so the code block executes.

On the other hand, checking for inequality:

string1="smart"
string2="tech101"

if [[ "$string1" != "$string2" ]]; then
    echo "Strings are not equal"
fi

Output:

Strings are not equal

Since the two strings are not equal, the code block within the if statement runs.

Lexicographic String Comparison

You can compare strings lexicographically using operators like < and > to determine their order. For example, comparing string1 and string2:

string1="smart"
string2="tech101"

if [[ "$string1" < "$string2" ]]; then
    echo "string1 sorts before string2"
fi

Output:

string1 sorts before string2

In this scenario, "smart" appears before "tech101" in lexicographic order, so the code block executes.

Similarly, if string1 is lexicographically greater than string2:

string1="tech101"
string2="smart"

if [[ "$string1" > "$string2" ]]; then
    echo "string1 sorts after string2"
fi

Output:

string1 sorts after string2

Here, "tech101" comes after "smart," so the code block runs.

Conclusion

That's all folks. Thanks for staying this long. If you any question and/or comments put them below. If you want to learn more, read the official documentation about the conditional statements in bash over here.