Bash Scripting Tutorial – Linux Shell Script and Command Line for Beginners

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:

ImageChecking 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:

ImageYou 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 the date 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 variable the_path.
  • Line #8: The ls command uses the path stored in the the_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 🚀

Image

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:

  1. Assign the value directly like this:
country=Pakistan
  1. 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:

  1. Variable names should begin with a letter or an underscore (_).
  2. They can include letters, numbers, and underscores (_).
  3. Variable names are case-sensitive.
  4. Spaces or special characters should not be included in variable names.
  5. Choose descriptive names that clearly indicate the variable’s purpose.
  6. 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
Image
  1. 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
  1. 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:

Image

Displaying output

Here, we’ll explore several methods for receiving output from scripts.

  1. Displaying output in the terminal:
echo "Hello, World!"

This displays the text “Hello, World!” in the terminal.

  1. 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.

  1. Adding to a file:
echo "More text." >> output.txt

This appends the text “More text.” to the end of the output.txt file.

  1. 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:

  1. cd: Change to a different directory.
  2. ls: List the contents of the current directory.
  3. mkdir: Create a new directory.
  4. touch: Create a new file.
  5. rm: Remove a file or directory.
  6. cp: Copy a file or directory.
  7. mv: Move or rename a file or directory.
  8. echo: Print text to the terminal.
  9. cat: Concatenate and display the contents of a file.
  10. grep: Search for a pattern within a file.
  11. chmod: Modify the permissions of a file or directory.
  12. sudo: Execute a command with administrative privileges.
  13. df: Show the available disk space.
  14. history: Display a list of previously executed commands.
  15. 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 🚀

Image

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
Image

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
Image

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:

ScheduleDescriptionExample
0 0Schedule a script to run at midnight every day.0 0 /path/to/script.sh
/5Schedule a script to run every 5 minutes./5 /path/to/script.sh
0 6 1-5Schedule a script to run at 6 AM, Monday through Friday.0 6 1-5 /path/to/script.sh
0 0 1-7Schedule a script to run on the first 7 days of every month.0 0 1-7 /path/to/script.sh
0 12 1Schedule 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.

Leave a Comment