Conditions in bash scripting (if statements)

Gudisa Gebi
12 min readApr 1, 2024

--

Introduction

What is Bash Scripting

What is Bash scripting? Before we start, let’s make sure we’re all on the same page. What is Bash scripting?

Imagine you’re a conductor leading an orchestra. Each musician knows how to play their instrument, but it’s up to you, the conductor, to bring them all together in harmony. Similarly, Bash scripting is like being the conductor of your computer. You write a series of instructions in a text file (like sheet music), and when you run the script, Bash (the conductor) follows those instructions, orchestrating various tasks or commands on your computer (like the musicians playing their instruments).

Just like the musicians need their instruments tuned before a performance, you need to ensure your script is properly set up before running it. This means giving it permission to execute, much like tuning the instruments before the show begins.

Conditions

Think of Bash scripting and its if statements like following a recipe. Imagine you’re cooking in a kitchen and you have a recipe card with instructions. Each step might have conditions attached, like “if the water is boiling, add the pasta”.

In Bash, it’s similar. You write instructions (commands) in your script, and sometimes you only want certain commands to run if certain conditions are met. So, it’s like checking your recipe card before adding ingredients or performing actions.

For instance:

if [ $eggs -ge 3 ]; then
echo "You have enough eggs to make an omelette!"
else
echo "You need at least 3 eggs to make an omelette. Go buy some!"
fi

Here, you’re checking if you have enough eggs to make an omelette. If you do, you proceed with cooking; otherwise, you go get more eggs.

Just as recipes have their own language and terminology (like “whisk until stiff peaks form”), Bash has its own syntax (like using -ge for "greater than or equal to"). It's like following the rules of a cooking recipe to get the desired outcome.

Understanding the Syntax of Bash if Statements

In Bash scripting, if statements are fundamental for making decisions based on conditions. Let's break down the basic syntax:

if <condition>; then
<commands>
fi
  • if: This keyword initiates the conditional statement.
  • <condition>: Here, we place the condition we want to evaluate. It could be a comparison or a test.
  • then: Marks the beginning of the block of commands that will execute if the condition is true.
  • <commands>: These are the instructions to execute if the condition evaluates to true.
  • fi: This signals the end of the if statement block.

You can also include an else block to handle situations when the condition is false:

if <condition>; then
<commands>
else
<other_commands>
fi

And for multiple conditions, you can use elif:

if <condition1>; then
<commands>
elif <condition2>; then
<other_commands>
else
<fallback_commands>
fi

Let’s illustrate this with an example:

if [ -r somefile ]; then
content=$(cat somefile)
elif [ -f somefile ]; then
echo "The file 'somefile' exists but is not readable to the script."
else
echo "The file 'somefile' does not exist."
fi
  • First Condition ([ -r somefile ]): Checks if the file somefile is readable. If true, it reads the file's content.
  • Second Condition (elif [ -f somefile ]): If the first condition is false, it checks if the file exists but isn't readable.
  • Else (else): If both conditions are false, it reports that the file doesn't exist.

Understanding these structures helps in creating robust and flexible Bash scripts for automating tasks effectively.

Comparison Operators:

  • In Bash scripting, we often need to compare values to make decisions.
  • Common comparison operators include -eq (equal), -ne (not equal), -gt (greater than), -lt (less than), -ge (greater than or equal to), and -le (less than or equal to).
  • These operators are used within square brackets [ ] to form conditional expressions.

Using -ge and -lt:

  • -ge stands for "greater than or equal to," and -lt stands for "less than."
  • These operators are used to compare numerical values.
  • For example, if [ $age -ge 18 ]; then echo "You are an adult"; fi checks if the variable $age is greater than or equal to 18, and if true, it prints "You are an adult."
  • Similarly, if [ $score -lt 60 ]; then echo "You failed"; fi checks if the variable $score is less than 60, and if true, it prints "You failed."

Practical Example:

  • Let’s say we want to check the number of apples in a basket.
apples=12  # Assuming there are 12 apples
if [ $apples -eq 10 ]; then
echo "You have exactly 10 apples!"
elif [ $apples -ne 0 ]; then
echo "You have some apples, but not exactly 10."
fi

if [ $apples -gt 10 ]; then
echo "You have more than 10 apples!"
elif [ $apples -lt 10 ]; then
echo "You have less than 10 apples!"
else
echo "You have exactly 10 apples!"
fi
if [ $apples -ge 10 ]; then
echo "You have 10 or more apples!"
fi
if [ $apples -le 10 ]; then
echo "You have 10 or fewer apples!"
fi

In this example:

  • We first check if the number of apples is exactly 10 using -eq.
  • Then, we use -ne to check if the number of apples is not equal to 0 (implying there are some apples).
  • Next, we use -gt and -lt to check if the number of apples is greater than or less than 10, respectively.
  • We also demonstrate -ge to check if the number of apples is greater than or equal to 10, and -le to check if it's less than or equal to 10.

These comparisons allow us to provide different responses based on the quantity of apples in the basket.

The basic rules of bash conditions

When you start writing and using your own conditions, there are some rules you should know to prevent getting errors that are hard to trace. Here follow three important ones:

1.Spaces between Brackets and Checks:

When using conditions in bash scripting, it’s essential to include spaces between the square brackets [ ] and the actual check or comparison inside. Incorrect: if [$foo -ge 3]; then Correct: if [ $foo -ge 3 ]; then Explanation: Without the spaces, bash won’t recognize the condition properly and will throw errors.

# Incorrect:
if [$foo -ge 3]; then

# Correct:
if [ $foo -ge 3 ]; then

2. Terminate Lines Before Keywords:

  • Each keyword like “if,” “then,” “else,” “elif,” and “fi” must start on a new line or be preceded by a semicolon ;.
  • Incorrect:
  • if [ $foo -ge 3]then
  • Correct:
  • if [ $foo -ge 3 ]; then
  • Explanation: Terminating the line before the keyword ensures proper parsing by bash and prevents syntax errors.

3. Quoting String Variables:

  • It’s a good habit to quote string variables to prevent issues with spaces or newlines.
  • Incorrect: if [ $stringvar == "tux" ]; then
  • Correct: if [ "$stringvar" == "tux" ]; then
  • Explanation: Quoting the variable ensures that it’s treated as a single entity, especially when it contains spaces or special characters.

These rules are fundamental for writing robust and error-free bash scripts. They help ensure that your conditions are interpreted correctly and prevent common pitfalls that can lead to difficult-to-trace errors.

Different condition Syntaxes

1. Single-bracket syntax

The single-bracket syntax is the original and oldest supported syntax for conditions in Bash. It supports three main types of conditions:

Certainly! Let’s break down the single-bracket syntax and provide examples for each type of condition:

  1. File-based conditions:

This type of condition allows different kinds of checks on a file.

Example:

if [ -L symboliclink ]; then

echo "The file 'symboliclink' exists and is a symbolic link."

fi

  • Explanation:
  • -L checks if the file symboliclink exists and is a symbolic link.
  • If true, the message “The file ‘symboliclink’ exists and is a symbolic link.” is echoed.

2.String-based conditions:

  • This type allows checks on a string and comparing of strings.
  • Example one:
if [ -z "$emptystring" ]; then
echo "The string is empty or uninitialized."
fi

Explanation:

  • -z checks if the variable $emptystring is an empty string or uninitialized.
  • If true, the message “The string is empty or uninitialized.” is echoed.

Example two:

if [ "$stringvar1" == "cheese" ]; then

echo "The variable contains the string 'cheese'."

fi

  • Explanation:
  • == checks if the variable $stringvar1 contains the string "cheese".
  • If true, the message “The variable contains the string ‘cheese’.” is echoed.

3.Arithmetic (number-based) conditions:

  • This type allows comparing integer numbers.
  • Example:
  • if [ $num -lt 1 ]; then echo "The number is less than 1." fi
  • -lt checks if the variable $num is less than 1.
  • If true, the message “The number is less than 1.” is echoed.

These examples illustrate the usage of the single-bracket syntax in bash scripting for different types of conditions: file-based, string-based, and arithmetic conditions.

2. Double-bracket syntax

You might have seen conditions enclosed in double square brackets, like this:

if [[ "$stringvar" == *string* ]]; then

These double brackets are an improved version of the single-bracket syntax. They have mostly the same features but with some important differences. Here’s a simple explanation of those differences.

1.Shell Globbing in String Comparisons:

  • In the double-bracket syntax, you can use shell globbing patterns like * to match parts of strings.

Imagine you have a string of text stored in a variable called stringvar. Now, you want to check if this string contains a particular word, let's say "string".

In the example

if [[ "$stringvar" == *string* ]]; then  
echo "The variable contains the word 'string' anywhere."
fi

Here’s what’s happening:

[[ "$stringvar" == *string* ]]: This part is like asking a question. You're checking if the value stored in the variable stringvar contains the word "string" anywhere inside it.

*string*: The * symbol acts as a wildcard here. It basically means "any sequence of characters". So, *string* matches any sequence of characters that have "string" in them. For example, it would match "thisis a string here" or "anotherstring" or even just "string" by itself.

If the condition [[ "$stringvar" == *string* ]] is true, it means that "string" is found anywhere within the value of stringvar. So, the code inside the if statement executes, which in this case is just printing a message: "The variable contains the word 'string' anywhere.".

So, in simple terms, this code checks if the variable stringvar contains the word "string" anywhere in it, and if it does, it prints a message.

2.Prevention of Word Splitting:

In Bash scripting, when you have a string variable that contains spaces, using the double-bracket syntax [[ ... ]] prevents the string from being split into separate words based on those spaces.

  • Here’s an example:
if [[ $stringvarwithspaces != foo ]]; then     
echo "The variable contains something other than 'foo'."
fi

Here’s what’s happening:

[[ $stringvarwithspaces != foo ]]: This is a condition checking if the value of the variable stringvarwithspaces is not equal to "foo".

$stringvarwithspaces: This is the variable you're checking. It contains spaces in its value.

!= foo: This part checks if the value of stringvarwithspaces is not equal to "foo".

The important thing here is that because we’re using double brackets [[ ... ]], Bash doesn't split the string variable stringvarwithspaces into separate words based on spaces. Instead, it treats the entire variable as a single entity.

This is useful because it simplifies conditionals involving string variables with spaces. You don’t need to worry about quoting the variable to prevent word splitting, which can make your code cleaner and easier to read.

3.No Filename Expansion:

In Bash scripting, when you use double-bracket syntax [[ ... ]], it treats patterns like *.sh as literal strings instead of trying to expand them to match filenames in the current directory.

  • Here’s an example:
if [[ -a *.sh ]]; then
echo "There is a file with a .sh extension."
fi

Here’s what’s happening:

[[ -a *.sh ]]: This is a condition checking if there is a file in the current directory with a ".sh" extension.

-a: This is a test operator that checks if a file exists. It's followed by the pattern *.sh.

*.sh: This is the pattern being checked. It's looking for files with a ".sh" extension.

One common task in Bash scripting is to check for the existence of files with specific extensions, such as “.sh” files. In the context of conditional expressions, you might want to check if there are any files with the “.sh” extension in the current directory before proceeding with a certain action.

However, when you include a pattern like *.sh inside double brackets, it doesn't behave the same way as it does outside of the double brackets. Normally, in Bash, when you use a pattern like *.sh, it gets replaced by a list of filenames ending with ".sh" before executing the command. This process is called filename expansion or globbing.

For example, if you run ls *.sh in the terminal, Bash will first replace *.sh with a list of filenames ending in ".sh" in the current directory before executing the ls command.

But inside double brackets, Bash treats patterns like *.sh as literal strings, not as patterns to be expanded into filenames. This means that *.sh remains as it is, without being replaced by any filenames, and the comparison or condition operates on the pattern itself rather than the filenames it could potentially match.

So, if you use [[ -a *.sh ]] to check for the existence of ".sh" files, Bash won't actually look for files with the ".sh" extension. Instead, it will treat *.sh as a plain string and evaluate the condition accordingly.

To achieve the desired behavior of checking for files with a specific extension inside double brackets, you would need to use a different approach, such as using the find command or iterating over the filenames in the directory and performing the check individually.

In summary, while double brackets [[ ... ]] offer many advantages for conditional expressions in Bash scripting, they don't interpret patterns like *.sh in the same way as they are interpreted outside of the double brackets. This difference in behavior is important to understand when writing scripts that involve file operations and conditional checks.

4.More Combining Expressions:

In Bash scripting, double brackets [[ ... ]] allow you to use common logical operators like && (logical AND) and || (logical OR) to combine multiple conditions within a single statement.

  • For instance, consider the following example:
if [[ $num -eq 3 && "$stringvar" == foo ]]; then
echo "The number is 3 and the string is 'foo'."
fi

In this example:

&& represents logical AND. It means that both conditions must be true for the overall expression to be true.

$num -eq 3 checks if the variable num is equal to 3.

"$stringvar" == foo checks if the variable stringvar contains the string "foo".

So, the if statement will only execute the echo command if both conditions are true: if the value of num is 3 and if the value of stringvar is "foo".

Using && and || in double brackets allows you to write more concise and readable conditionals, especially when you need to combine multiple conditions to determine whether a certain action should be taken.

5.Regex Pattern Matching with =~ Operator:

  • In Bash scripting, the double-bracket syntax [[ ... ]] introduces the =~ operator, which allows you to perform pattern matching using regular expressions.
  • For example:
if [[ "$stringvar" =~ pattern ]]; then
echo "The variable matches the pattern."
fi

Here’s what’s happening:

  • =~ is the operator used for pattern matching with regular expressions.
  • "pattern" is a placeholder for the regular expression you want to match against.
  • $stringvar is the variable you want to check against the pattern.

Regular expressions are special sequences of characters that help you define search patterns. They allow for sophisticated matching criteria, such as matching specific characters, ranges of characters, or repeating patterns.

By using the =~ operator, you can perform complex string analysis and manipulation based on these regex patterns. It provides more flexibility than simple string comparisons and allows you to create more advanced conditional statements in your Bash scripts.

These features make double-bracket syntax a versatile and powerful tool for writing robust and expressive conditionals in bash scripts.

3. Double-parenthesis syntax

In shell scripting, particularly in environments like Korn shell, there exists a concise and powerful syntax for arithmetic conditions: the double-parenthesis syntax. It offers a more familiar structure for programmers and supports a range of operators for numeric comparisons.

Consider the following example:

if (( $num <= 5 )); then
# Code block executed if $num is less than or equal to 5
fi

This condition evaluates to true if the variable $num is less than or equal to 5. It resembles traditional programming languages and supports standard operators like ==, <, >=, and others.

The double-parenthesis syntax also accommodates logical combinations using && and ||, making complex conditions more readable and manageable. However, it does not support -a and -o as logical operators.

Internally, this syntax is equivalent to the built-in let command, providing a streamlined approach to arithmetic evaluations in shell scripts.

Conclusion

mastering conditions in Bash scripting opens up a world of possibilities for automating tasks efficiently and robustly. Whether you’re checking file existence, comparing strings, or evaluating numerical values, understanding the syntax and various operators empowers you to create sophisticated scripts tailored to your needs.

By following best practices such as proper spacing, line termination, and quoting string variables, you can ensure your scripts are error-free and maintainable. Whether you opt for the traditional single-bracket syntax, the enhanced capabilities of double brackets, or the concise arithmetic expressions of double parentheses, each offers unique advantages depending on the context of your script.

Moreover, the flexibility provided by features like shell globbing, prevention of word splitting, and regex pattern matching with double brackets allows you to tackle complex scenarios with ease. Whether you’re a novice or an experienced scripter, these tools enable you to write cleaner, more readable code that can handle a wide range of conditions and scenarios.

DAY-1

DAY-2

DAY-3

--

--