If you are a system administrator responsible for maintaining tens or hundreds of Linux servers, manually updating each server or applying configurations would not only be time-consuming but also prone to errors. This is where Bash scripting comes in handy. You can write a script to automate updates, backups, or configurations across multiple servers simultaneously.
For instance, instead of logging into each server individually, a single Bash script could remotely log in, apply updates, and even send notifications of completed tasks. Wonderful, isn’t it. But if you are a beginner starting your Linux journey, you might have a question like “What is a Bash Script?” or “How Bash Scripting Can Help Automate Things?”
In this guide, we will start with the basics and understand Bash Script, syntax and key concepts of Bash Scripting, and even some practical examples.
Outline
ToggleWhat Is A Bash Script?
Bash, short for “Bourne Again Shell,” is one of the most popular and widely-used shell or command-line interfaces (CLI) in Unix-based and Unix-like systems (including Linux). It is based on the Bourne Shell (simply known as sh). The purpose of Bash (or any shell for that matter) is to provide a text-based interface (nowadays graphical user interface as well) for the user to interact with the system.
When we say “interact”, it can be anything: typing on the keyboard puts letters on the screen, moving the mouse will change the position of the cursor, or a simple command to delete a file will clear the bytes on the hard disk.
Apart from system interaction, Bash has one other important functionality: the ability to execute commands in a sequence, with or without user interaction. The file that contains this series of commands is known as a Bash Script.
Most Linux distributions come pre-installed with Bash. As a result, Linux users (regular users as well as system administrators) can automate nearly any task performed in the terminal by using Bash scripts. Whether it is file manipulation, system monitoring and maintenance, log rotation, or installing software packages, Bash scripts can execute these commands with minimal to no input from the user.
Basic Shell Commands
The terminal operates through a set of commands that the shell interprets. Bash, as the default shell in most Unix-based systems, processes these commands to perform a variety of tasks. Some of the most common commands you will use are:
- cd: Changes the current directory. For example, cd /home/user navigates to the specified directory.
- ls: Lists the files and directories within the current directory. Running ls -l provides detailed information about the contents.
- cp: Copies files or directories. For example, cp file1.txt /backup/ copies a file to a new location.
- mv: Moves or renames files and directories. The command mv file1.txt file2.txt renames the file, while mv file1.txt /backup/ moves it to a different directory.
- cat: Concatenates and displays the content of a file. Running cat file.txt shows the entire file’s contents on the terminal.
- echo: Displays a line of text or the value of a variable. For example, echo “Hello, World!” prints the text within the quotes to the terminal.
Hello World: Your First Bash Script
Create A Simple Bash Script
To write your first Bash script, you have to create a file and add a few simple commands. To do this, you need a text editor like nano, vim, or gedit. Open your terminal and create a new file called hello.sh. You can use the following command to do this:
nano hello.sh
Inside this file, we will write a short script that prints “Hello, World!” to the terminal. The script starts with a special line (known as Shebang), followed by the echo command to print text. Below is the content you should add to the file:
#!/bin/bash
echo “Hello, World!”
This script may look simple, but it teaches two foundational concepts: how to specify which shell should run the script and how to display output in the terminal.
What Is Shebang?
The first line of your script, #!/bin/bash, plays an important role in telling the system how to execute the script. This line, called the “Shebang,” starts with #! followed by the path to the Bash interpreter.
In this case, the path is /bin/bash, which is the default location of Bash on most Unix-like systems (Linux and macOS). When the system encounters this line, it knows that the rest of the file should be interpreted using Bash.
The Shebang is important because Unix systems often have multiple shells installed. Aside from Bash, users may work with shells like Zsh (Z Shell), Csh (C Shell), or Fish (Friendly Interactive Shell).
If the system doesn’t know which interpreter to use, it could misinterpret the commands and fail to execute them properly. By specifying #!/bin/bash at the beginning of the file, you are explicitly directing the system to use the Bash interpreter.
On most Linux distributions and macOS, Bash resides in /bin/bash. However, some systems may use a different path, such as /usr/bin/bash or /usr/local/bin/bash. To accommodate these variations, some scripts use #!/usr/bin/env bash, which searches for the Bash interpreter in the system’s environment. This makes the script more portable across different Unix-based platforms.
Grant Execution Permission
After creating your Bash script, it exists as a simple text file. Although the script contains valid commands, the system will not execute it until you exclusively grant the necessary permissions.
For a script to become executable, the system needs explicit instructions through the chmod command.
In the terminal, use the command chmod +x to modify the file’s permissions to make it executable. The +x option stands for “adding execution permission.” To apply this to your script, you would run the following command:
chmod +x hello.sh
Run The Script
After making your script executable, the next step is to run it in the terminal. To do this, use the command ./scriptname.sh. In this case, if you created a script called hello.sh, you would enter:
./hello.sh
The ./ part of this command tells the system to look for the script in the current directory. Unix-based systems often separate executable files into specific system directories like /bin or /usr/bin. If you don’t specify ./, the terminal may not find your script and assume it doesn’t exist, even though it is in the same folder you’re working in.
Running ./hello.sh executes the script by feeding it to the Bash interpreter, which processes the commands line by line. In this example, the echo command gets executed and the message “Hello, World!” appears on the screen.
Basic Concepts In Bash Scripting With Syntax
Variable
Variables in Bash allow you to store and manage dynamic data within your scripts. By defining variables, you can reuse values, modify them, and make your script more flexible. The syntax for declaring a variable in Bash is:
NAME=”John”
Here, the variable NAME holds the value “John.” Unlike many programming languages, Bash does not require data types when declaring variables. It treats everything as a string by default.
One key point to remember is that there must be no spaces between the variable name, the equal sign, and the value. If spaces are present, the script will not interpret the variable correctly.
In order to access the value of a variable, you need to use the $ symbol. For example, to print the value of NAME, you can use:
echo $NAME
This would output “John” to the terminal.
By using variables, you can easily change the behavior of a script without hard-coding values. For instance, instead of writing the same file path multiple times, you can assign the path to a variable and reference it throughout the script. Here’s another example. You can capture user input and store it in a variable, then use that input later in the script.
read -p “Enter your name: ” USERNAME
echo “Hello, $USERNAME!”
Here, the read command captures input and assigns it to the variable USERNAME.
Input
In Bash scripting, the read command handles input by capturing text entered by the user during script execution. This input can then be stored in a variable for later use within the script. The syntax for using read is straightforward:
read VARIABLE_NAME
Here, the user’s input will be stored in the variable VARIABLE_NAME. For example, consider the following script that prompts the user to enter their name:
#!/bin/bash
echo “Enter your name:”
read NAME
echo “Hello, $NAME!”
When executed, this script prompts the user for their name, stores the input in the NAME variable, and then greets the user by name.
The read command also accepts options like -p (which displays a prompt on the same line as the input request) or -s for silent input (useful when entering sensitive data like passwords). Here’s an example using the -p option:
#!/bin/bash
read -p “Enter your favorite color: ” COLOR
echo “Your favorite color is $COLOR.”
Output
The echo command primarily handles the output in Bash scripting. It prints text or variables to the terminal. The basic syntax for echo is:
echo “Your message here”
This command prints the text within the quotes. For example:
#!/bin/bash
echo “Hello, World!”
This script prints “Hello, World!” to the terminal. You can also use echo to display the values of variables.
#!/bin/bash
NAME=”John”
echo “Hello, $NAME!”
In addition to plain text, you can format the output of echo by including special characters such as newlines (\n) or tabs (\t). To enable these characters, use the -e option.
#!/bin/bash
echo -e “Hello,\nWelcome to Bash scripting!”
This command prints “Hello” on one line and “Welcome to Bash scripting!” on the next line.
You can also redirect the output to files in Bash using echo. For instance, you can write the result of a script to a file rather than displaying it on the terminal. This is achieved with the > operator:
echo “This will be written to a file” > output.txt
This command writes the text to the file output.txt. If the file doesn’t exist, it creates one (or overwrites it if it does). If you want to append the output instead of overwriting the file, use the >> operator.
echo “This will be appended” >> output.txt
Comments
No matter how big a developer you are, always add comments to your code. They make your code readable and maintainable. Although Bash scripts are often short and simple, adding comments helps clarify the purpose and function of different sections of the code.
In Bash, single-line comments begin with the # symbol. Everything following the # on that line will be ignored by the interpreter. Here is an example of a comment in a script:
# This script prints a greeting
echo “Hello, World!”
Conditional Statements (if-else)
Decision-making or conditional statements in Bash allow scripts to make decisions based on specific conditions. The if-else statement is very popular and you can execute different sets of commands depending on whether a condition is true or false.
The basic syntax for an if-else statement in Bash looks like this:
if [ condition ]; then
# commands to execute if true
else
# commands to execute if false
fi
Conditions often involve checking file existence, comparing strings, or evaluating numerical values. For example, the condition [ -f “/path/to/file” ] checks if a file exists at the specified path. If the file exists, the script can perform specific actions. Here’s the code for this example:
if [ -f “/path/to/file” ]; then
echo “File exists.”
else
echo “File does not exist.”
fi
You can extend these conditional checks to more complex scenarios. For instance, you might want to compare two strings or evaluate numeric conditions. Bash provides operators for these comparisons (-eq for numbers or == for strings).
Loops (For And While)
If you want to automate repetitive tasks by executing a block of code multiple times, loops are the way to go. The two main types of loops are: for and while.
The for loop syntax in Bash is:
for i in {1..5}; do
echo “Number: $i”
done
This loop iterates from 1 to 5, printing “Number: $i” for each value of i.
The while loop is another way to perform repetitive tasks. The syntax for a while loop looks like this:
while [ condition ]; do
# commands to execute while the condition is true
done
In a while loop, the commands inside the loop continue executing as long as the condition remains true. This type of loop is very useful when waiting for a specific system state, like waiting for a file to appear or a process to complete.
For example, a while loop could keep checking for a file’s existence:
while [ ! -f “/path/to/file” ]; do
echo “Waiting for file…”
sleep 10
done
echo “File found.”
This loop checks for the file every 10 seconds, printing “Waiting for file…” until the file appears. Once the file is detected, the script exits the loop and prints “File found.”
Functions
Basic Syntax Of Functions
You can group a set of commands into blocks and reuse them multiple times with the help of functions. By defining a function, you can call it anywhere in your script without rewriting the same code. The basic syntax for defining a function is:
function_name() {
# Commands
}
To call the function, simply use its name. Here’s a simple example that defines and calls a function:
#!/bin/bash
greet() {
echo “Hello, $1!”
}
greet “Alice”
In this example, the function greet prints a personalized greeting. The function takes one argument, $1, which represents the first argument passed to the function. When calling greet “Alice”, the script outputs “Hello, Alice!”
Functions can accept multiple arguments, each accessible by numbered variables such as $1, $2, and so on. Here’s a function that calculates the sum of two numbers:
#!/bin/bash
add_numbers() {
SUM=$(($1 + $2))
echo “The sum is: $SUM”
}
add_numbers 5 10
The function add_numbers takes two arguments, adds them, and prints the result. Calling add_numbers 5 10 will output “The sum is: 15.”
Functions For Repeated Tasks
Instead of duplicating the same code block multiple times, you define the logic once in a function and call it whenever needed. For example, suppose you are working on a script that needs to perform regular backups. Instead of writing the backup logic repeatedly, you can place it inside a function:
#!/bin/bash
backup_files() {
cp /path/to/source/* /path/to/destination/
echo “Backup completed.”
}
# Call the function at different points in the script
backup_files
Here, the backup_files function copies files from the source directory to the destination and prints a confirmation message. If you need to back up files multiple times in the script, simply call backup_files wherever necessary.
Additionally, functions in Bash can return values by using the return command or by outputting a value that can be captured by the caller. Here’s an example of a function returning the exit status of a command:
#!/bin/bash
check_file() {
if [ -f “$1” ]; then
return 0 # File exists
else
return 1 # File does not exist
fi
}
check_file “/path/to/file”
if [ $? -eq 0 ]; then
echo “File found.”
else
echo “File not found.”
fi
This check_file function checks if a file exists and returns an appropriate exit code. The calling script captures this exit status using $? and reacts based on whether the file was found.
Practical Bash Script Example
Here is a Bash script that will perform a file backup. It will allow the user to customize options, such as choosing the directory to back up and setting a backup destination. Additionally, the script will also include error checking. Here’s a brief overview of the script:
- Ask the user for the source directory and destination directory.
- Check if both directories exist.
- If the directories exist, it will back up the files from the source to the destination.
- Output the status of each step to the user.
- Include a loop to provide an option to back up additional directories.
- Use functions to repeat tasks (checking the existence of directories and performing the backup).
#!/bin/bash
# A script to back up files from one directory to another
# Function to check if a directory exists
check_directory() {
if [ -d “$1” ]; then
return 0 # Directory exists
else
return 1 # Directory does not exist
fi
}
# Function to perform the backup
backup_files() {
cp -r “$1″/* “$2”
echo “Backup completed from $1 to $2”
}
# Main script starts here
echo “Welcome to the Backup Script!”
# Loop to allow multiple backups
while true; do
# Input: Ask for the source directory
read -p “Enter the directory you want to back up: ” SOURCE_DIR
# Input: Ask for the destination directory
read -p “Enter the backup destination directory: ” DEST_DIR
# Check if the source directory exists
check_directory “$SOURCE_DIR”
if [ $? -ne 0 ]; then
echo “Source directory does not exist. Please try again.”
continue # Skip to the next loop iteration
fi
# Check if the destination directory exists
check_directory “$DEST_DIR”
if [ $? -ne 0 ]; then
echo “Destination directory does not exist. Please try again.”
continue # Skip to the next loop iteration
fi
# Output: Inform the user about the backup process
echo “Backing up files from $SOURCE_DIR to $DEST_DIR…”
# Perform the backup using the function
backup_files “$SOURCE_DIR” “$DEST_DIR”
# Ask the user if they want to perform another backup
read -p “Do you want to back up another directory? (yes/no): ” RESPONSE
# Conditional statement: Check the user’s response
if [ “$RESPONSE” != “yes” ]; then
echo “Exiting the backup script.”
break # Exit the loop
fi
done
echo “Script finished. Goodbye!”