Process automation in Linux often relies on shell scripting, where a file containing a sequence of commands is executed as a single script.
In this guide, we’ll cover the fundamentals of bash scripting, including variables, commands, input/output, and debugging, with examples provided throughout.
Let’s get started. 🚀
Pre-requisites
To follow this tutorial, make sure you have:
- A running Linux environment with access to the command line.
If you don’t have Linux installed or are new to it, you can quickly access the Linux command line using Replit, a browser-based IDE that lets you use the bash shell in just a few minutes.
Alternatively, you can install Linux on your system as well.
Introduction
Definition of Bash scripting
A bash script is a file that contains a sequence of commands executed line by line by the bash program. It allows you to perform tasks like navigating to a directory, creating folders, and launching processes using the command line.
By storing these commands in a script, you can easily repeat the same steps multiple times by simply running the script.
Advantages of Bash scripting
Bash scripting is a powerful and flexible tool for automating system administration, managing resources, and handling routine tasks on Unix/Linux systems. Here are some benefits of using shell scripting:
- Here are some key benefits of using shell scripting:
- Automation: Shell scripts help automate repetitive tasks, saving time and reducing the risk of errors from manual execution.
- Portability: Shell scripts can run across multiple platforms, including Unix, Linux, macOS, and even Windows with the help of emulators or virtual machines.
- Flexibility: Highly customizable, shell scripts can be easily tailored to meet specific needs and combined with other programming languages or tools for more advanced functionality.
- Accessibility: Shell scripts are easy to create using any text editor, and most systems come with a built-in shell interpreter, requiring no special software.
- Integration: Shell scripts can work seamlessly with other tools and applications, such as databases, web servers, and cloud services, enabling complex automation and system management.
- Debugging: Shell scripts are straightforward to debug, with built-in debugging and error-reporting features available in most shells for quick issue resolution.
Overview of Bash shell and command line interface
The terms ‘shell’ and ‘bash’ are often used interchangeably, but there is a subtle difference between them.
A shell is a program that provides a command-line interface for interacting with the operating system. Bash (Bourne-Again SHell) is one of the most widely used Unix/Linux shells and is the default shell in many Linux distributions.
A shell or command-line interface typically looks like this.
The shell receives commands from the user and displays the output.
In the example above, zaira@Zaira
is the shell prompt. When used interactively, the shell shows a $
symbol, indicating it is ready for a command.
If the shell is running as the root user (with administrative privileges), the prompt changes to #
. The root user prompt typically looks like this.
[root@host ~]#
While Bash is a popular type of shell, there are several other shells available, including Korn shell (ksh
), C shell (csh
), and Z shell (zsh
). Each shell has its own syntax and unique features, but they all serve the same purpose: providing a command-line interface to interact with the operating system.
To find out which shell you are using, you can use the ps
command:
ps
Here is the output for me:
Checking the shell type. I’m using bash shell Although Bash is a widely used shell, other options include Korn shell (ksh
), C shell (csh
), and Z shell (zsh
). Each of these shells has its own syntax and features, but they all provide a command-line interface for interacting with the operating system.
To check which shell you are currently using, run the ps
command:
How to Get Started with Bash Scripting
Running Bash commands from the command line
As mentioned earlier, the shell prompt usually appears as follows:
[username@host ~]$
After the $
sign, you can enter any command and view the output in the terminal.
Commands typically follow this syntax:
command [OPTIONS] arguments
Let’s explore some basic bash commands and review their outputs. Make sure to follow along! 🙂
date
: Displays the current date and time.
zaira@Zaira:~/shell-tutorial$ date
Tue Mar 14 13:08:57 PKT 2023
pwd
: Displays the current working directory.
zaira@Zaira:~/shell-tutorial$ pwd
/home/zaira/shell-tutorial
ls
: Displays the contents of the current directory.
zaira@Zaira:~/shell-tutorial$ ls
check_plaindrome.sh count_odd.sh env log temp
echo
: Prints a text string or the value of a variable to the terminal.
zaira@Zaira:~/shell-tutorial$ echo "Hello bash"
Hello bash
You can always access a command’s manual using the man
command.
For instance, the manual for ls
might look like this:
You can view detailed options for a command using man
.
How to Create and Execute Bash scripts
Script naming conventions
By convention, bash scripts typically end with .sh
. However, bash scripts can run without the .sh
extension just fine.
Adding the Shebang
Bash scripts begin with a shebang, which is a combination of the #
symbol and the exclamation mark !
, followed by the path to the bash shell. This line appears at the start of the script and instructs the shell to execute the script using bash. The shebang is essentially the absolute path to the bash interpreter.
Here is an example of a shebang statement:
#!/bin/bash
You can find the path to your bash shell (which may differ from the one above) using the following command:
which bash
Creating our first bash script
Our first script will prompt the user to enter a path, and in return, it will list the contents of that path.
Create a file named run_all.sh
using the vi
command, or any editor of your choice.
vi run_all.sh
Add the following commands to your file and save it:
#!/bin/bash
echo "Today is " `date`
echo -e "\nenter the path to directory"
read the_path
echo -e "\n you path has the following files and folders: "
ls $the_path
Let’s dive deeper into the script, examining it line by line. I’ll show the same script again, this time with line numbers.
1 #!/bin/bash
2 echo "Today is " `date`
3
4 echo -e "\nenter the path to directory"
5 read the_path
6
7 echo -e "\n you path has the following files and folders: "
8 ls $the_path
- Line #1: The shebang (
#!/bin/bash
) specifies the path to the bash shell. - Line #2: The
echo
command displays the current date and time on the terminal. Note that thedate
command is enclosed in backticks. - Line #4: We prompt the user to enter a valid path.
- Line #5: The
read
command captures the user input and stores it in the variablethe_path
. - Line #8: The
ls
command uses the path stored in thethe_path
variable to list the current files and folders.
Executing the bash script
To make the script executable, grant execution permissions to your user with the following command:
chmod u+x run_all.sh
Here,
chmod
changes the file permissions for the current user (:u
).+x
grants execution rights to the current user, allowing the file owner to run the script.run_all.sh
is the file we want to execute.
You can execute the script using any of the methods mentioned:
sh run_all.sh
bash run_all.sh
./run_all.sh
Let’s watch it run in action 🚀
Bash Scripting Basics
Comments in bash scripting
In bash scripting, comments begin with a #
. Any line starting with a #
is considered a comment and will be ignored by the interpreter.
Comments are useful for documenting code, and it’s a good practice to include them to help others understand your work.
Here are some examples of comments:
# This is an example comment
# Both of these lines will be ignored by the interpreter
Variables and data types in Bash
Variables allow you to store data, which can be read, accessed, and manipulated throughout your script.
Bash does not have specific data types. A variable in Bash can store numeric values, individual characters, or strings of characters.
Variables allow you to store data, which can be accessed, read, and manipulated throughout your script.
Bash does not use data types. A variable in Bash can store numeric values, individual characters, or strings of characters.
In Bash, you can assign and use variable values in the following ways:
- Assign the value directly like this:
country=Pakistan
- Assign the value based on the output from a program or command using command substitution. Remember, the
$
is necessary to access the value of an existing variable.
same_country=$country
To access the value of a variable, prefix the variable name with a $
.
zaira@Zaira:~$ country=Pakistan
zaira@Zaira:~$ echo $country
Pakistan
zaira@Zaira:~$ new_country=$country
zaira@Zaira:~$ echo $new_country
Pakistan
Variable naming conventions
In Bash scripting, the following conventions apply to variable naming:
- Variable names should begin with a letter or an underscore (_).
- They can include letters, numbers, and underscores (_).
- Variable names are case-sensitive.
- Spaces or special characters should not be included in variable names.
- Choose descriptive names that clearly indicate the variable’s purpose.
- Avoid using reserved keywords, such as
if
,then
,else
,fi
, and others, as variable names.
Here are some examples of valid variable names in Bash:
name
count
_var
myVar
MY_VAR
Here are some examples of invalid variable names:
2ndvar (variable name starts with a number)
my var (variable name contains a space)
my-var (variable name contains a hyphen)
By following these naming conventions, Bash scripts become more readable and easier to maintain.
Input and output in Bash scripts
Gathering input
In this section, we’ll explore different methods for providing input to our scripts.
Reading user input and storing it in a variable:
We can capture user input using the read
command.
#!/bin/bash
echo "What's your name?"
read entered_name
echo -e "\nWelcome to bash tutorial" $entered_name
- Reading from a file:
This code reads each line from a file called input.txt
and prints it to the terminal. We’ll cover while loops later in this article.
while read line
do
echo $line
done < input.txt
- Command line arguments
In a bash script or function, $1
represents the first argument passed, $2
represents the second argument, and so on.
This script accepts a name as a command-line argument and displays a personalized greeting.
echo "Hello, $1!"
We provided Zaira
as the argument to the script.
#!/bin/bash
echo "Hello, $1!"
Output:
Displaying output
Here, we’ll explore several methods for receiving output from scripts.
- Displaying output in the terminal:
echo "Hello, World!"
This displays the text “Hello, World!” in the terminal.
- Saving output to a file:
echo "This is some text." > output.txt
This writes the text “This is some text.” to a file named output.txt
. Note that the >
operator overwrites the file if it already contains content.
- Adding to a file:
echo "More text." >> output.txt
This appends the text “More text.” to the end of the output.txt
file.
- Redirecting the output:
ls > files.txt
This lists the files in the current directory and saves the output to a file named files.txt
. You can redirect the output of any command to a file using this method.
Basic Bash commands (echo, read, etc.)
Here is a list of some of the most frequently used bash commands:
cd
: Change to a different directory.ls
: List the contents of the current directory.mkdir
: Create a new directory.touch
: Create a new file.rm
: Remove a file or directory.cp
: Copy a file or directory.mv
: Move or rename a file or directory.echo
: Print text to the terminal.cat
: Concatenate and display the contents of a file.grep
: Search for a pattern within a file.chmod
: Modify the permissions of a file or directory.sudo
: Execute a command with administrative privileges.df
: Show the available disk space.history
: Display a list of previously executed commands.ps
: Show information about running processes.
Conditional statements (if/else)
Expressions that evaluate to either true or false are called conditions. There are various ways to assess conditions, such as using if
, if-else
, if-elif-else
, and nested conditionals.
Syntax:
if [[ condition ]];
then
statement
elif [[ condition ]]; then
statement
else
do this by default
fi
We can use logical operators like AND (-a
) and OR (-o
) to perform comparisons with greater significance.
if [ $a -gt 60 -a $b -lt 100 ]
Here’s an example of a Bash script that uses if
, if-else
, and if-elif-else
statements to check whether a user-inputted number is positive, negative, or zero:
#!/bin/bash
echo "Please enter a number: "
read num
if [ $num -gt 0 ]; then
echo "$num is positive"
elif [ $num -lt 0 ]; then
echo "$num is negative"
else
echo "$num is zero"
fi
The script begins by prompting the user to enter a number. It then uses an if
statement to check if the number is greater than 0. If so, the script outputs that the number is positive. If the number is not greater than 0, the script proceeds to an if-elif
statement to check if the number is less than 0. If it is, the script outputs that the number is negative. Lastly, if the number is neither greater than nor less than 0, the script uses an else
statement to indicate that the number is zero.
Watching it in action 🚀
Looping and Branching in Bash
While loop
While loops evaluate a condition and continue looping as long as the condition remains true. To control the loop’s execution, we need to include a counter statement that increments the counter.
In the example below, (( i += 1 ))
is the counter statement that increments the value of i
. The loop will run exactly 10 times.
#!/bin/bash
i=1
while [[ $i -le 10 ]] ; do
echo "$i"
(( i += 1 ))
done
For loop
The for
loop, like the while
loop, allows you to execute statements a specific number of times. However, they differ in their syntax and usage.
In the example below, the loop will run 5 times.
#!/bin/bash
for i in {1..5}
do
echo $i
done
Case statements
In Bash, case
statements are used to compare a given value with a list of patterns and execute a block of code based on the first matching pattern. The syntax for a case
statement in Bash is as follows:
case expression in
pattern1)
# code to execute if expression matches pattern1
;;
pattern2)
# code to execute if expression matches pattern2
;;
pattern3)
# code to execute if expression matches pattern3
;;
*)
# code to execute if none of the above patterns match expression
;;
esac
In this case, “expression” is the value we want to compare, while “pattern1”, “pattern2”, “pattern3”, and so on are the patterns we want to match it against.
The double semicolon ;;
separates each block of code that should execute for a matching pattern. The asterisk *
represents the default case, which runs if none of the specified patterns match the expression.
Here’s an example:
fruit="apple"
case $fruit in
"apple")
echo "This is a red fruit."
;;
"banana")
echo "This is a yellow fruit."
;;
"orange")
echo "This is an orange fruit."
;;
*)
echo "Unknown fruit."
;;
esac
In this example, since the value of “fruit” is “apple”, the first pattern matches, and the code block that echoes “This is a red fruit.” is executed. If the value of “fruit” were “banana”, the second pattern would match, and the code block that echoes “This is a yellow fruit.” would run, and so on. If the value of “fruit” doesn’t match any of the specified patterns, the default case will execute, echoing “Unknown fruit.”
How to Schedule Scripts using cron
Cron is a powerful tool for job scheduling available in Unix-like operating systems. By configuring cron, you can automate tasks to run on a daily, weekly, monthly, or at specific times. Its automation features are essential for efficient Linux system administration.
Here is the syntax for scheduling cron jobs:
# Cron job example
* * * * * sh /path/to/script.sh
In this syntax, the *
symbols represent minute(s), hour(s), day(s), month(s), and weekday(s), respectively.
Here are some examples of scheduling cron jobs:
Schedule | Description | Example |
---|---|---|
0 0 | Schedule a script to run at midnight every day. | 0 0 /path/to/script.sh |
/5 | Schedule a script to run every 5 minutes. | /5 /path/to/script.sh |
0 6 1-5 | Schedule a script to run at 6 AM, Monday through Friday. | 0 6 1-5 /path/to/script.sh |
0 0 1-7 | Schedule a script to run on the first 7 days of every month. | 0 0 1-7 /path/to/script.sh |
0 12 1 | Schedule a script to run at noon on the first day of every month. | 0 12 1 /path/to/script.sh |
Using crontab
The crontab
utility is used to add and edit cron jobs.
crontab -l
displays the cron jobs that are already scheduled for a specific user.
You can add or edit cron jobs using crontab -e
.
How to Debug and Troubleshoot Bash Scripts
Debugging and troubleshooting are crucial skills for any Bash scripter. Although Bash scripts can be very powerful, they can also encounter errors and unexpected behavior. In this section, we will explore some tips and techniques for effectively debugging and troubleshooting Bash scripts.
Set the set -x
option
A highly effective debugging technique for Bash scripts is to include the set -x
option at the beginning of the script. This activates debugging mode, which causes Bash to print each command it executes to the terminal, prefixed by a +
sign. This is extremely useful for pinpointing where errors occur in your script.
#!/bin/bash
set -x
# Your script goes here
Check the exit code
When Bash encounters an error, it sets an exit code to indicate the nature of the issue. You can check the exit code of the most recent command using the $?
variable. A value of 0
means the command was successful, while any other value indicates an error.
#!/bin/bash
# Your script goes here
if [ $? -ne 0 ]; then
echo "Error occurred."
fi
Use echo
statements
Another effective debugging technique in Bash scripts is to insert echo
statements throughout your code. This allows you to track where errors occur and observe the values being passed to variables.
#!/bin/bash
# Your script goes here
echo "Value of variable x is: $x"
# More code goes here
Use the set -e
option
To make your script exit immediately when any command fails, you can use the set -e
option. This will cause Bash to terminate with an error if any command in the script fails, helping you quickly identify and address issues.
#!/bin/bash
set -e
# Your script goes here
Troubleshooting crons by verifying logs
You can troubleshoot cron jobs by reviewing the log files. Logs are kept for all scheduled jobs, allowing you to check and verify whether a specific job ran as expected.
On Ubuntu/Debian, you can find cron logs at:
/var/log/syslog
The location may vary for other distributions.
A cron job log file might look like this:
2022-03-11 00:00:01 Task started
2022-03-11 00:00:02 Running script /path/to/script.sh
2022-03-11 00:00:03 Script completed successfully
2022-03-11 00:05:01 Task started
2022-03-11 00:05:02 Running script /path/to/script.sh
2022-03-11 00:05:03 Error: unable to connect to database
2022-03-11 00:05:03 Script exited with error code 1
2022-03-11 00:10:01 Task started
2022-03-11 00:10:02 Running script /path/to/script.sh
2022-03-11 00:10:03 Script completed successfully
Conclusion
In this article, we began by exploring how to access the terminal and running some basic Bash commands. We also learned about the Bash shell and briefly covered branching in code using loops and conditionals. Finally, we discussed automating scripts with cron and went over some troubleshooting techniques.