Beginners Guide to Shell Scripting

Subscribe to my newsletter and never miss my upcoming articles

Introduction

This tutorial will introduce you to scripting in a Bash shell, its structure, and how you can get started with it. In this tutorial, I assume that you are already familiar with or have some knowledge of the shell and Linux commands. If you don't, I found a really nice article on Linux commands by ToyinO, you can check it out.

Table of Contents

What is a Shell Script

A shell script is a command-line program that contains a series of commands that are executed by an interpreter. It is usually a plain text file that can contain a series of commands that will be run or executed by the Unix/Linux shell one after the other or sequentially. A shell script is great for automating tasks, it can help save time and increase productivity. If you have to repeatedly run a series of commands often, whether simple or complex to accomplish a task, then you can and probably should write a shell script for that task. You can put any command that you can type and run on the command line in a shell script and vice versa. Before you can run a script, you need to have execute permission for that script. You can name your script whatever you like but it is advisable that it be descriptive. You can add an extension on .sh to the name of the script but this is just so that it is clear to everyone that it is a script file, with Linux being an extensionless system, the script will still run without the .sh extension. Shell scripts can be used to automate tasks such as daily backups, installation of packages on multiple servers, troubleshooting, and so on.

Structure of a Shell Script

The first line of a shell script should indicate the interpreter that will be used to execute the script, in this case, I am using Bash. This line starts with something called Shebang, which is a # sign followed by an exclamation mark (!). The Shebang is then followed by the path to the interpreter.
Note - there are no spaces in the Shebang line.

#!/bin/bash

After the Shebang line, you can have comments describing what the script does, the name of the author, and even the day it was created. Anything following a # sign is a comment and is not being executed by the interpreter with exception of the Shebang line. Comments are optional but it is a good practice to include comments in your scripts.

# Name of Script: test.sh
# A simple bash script to illustrate some basic concepts
# Author: Goodness Chris-Ugari
# Date: 16-02-2021

After the comments at the file header with details of the script, you can then have your series of commands to be executed.

#!/bin/bash
# Name of Script: test.sh
# A simple bash script to illustrate some basic concepts
# Author: Goodness Chris-Ugari
# Date: 16-02-2021

# Display Hello, World
echo Hello, world

# Display the present/current working directory
pwd

To run the script above, you type out the path to the file and press enter. In this case, it would be ./test.sh, assuming that the script is located in our present working directory. When run the above script will print out "Hello, World" to the command line and print out your present working directory on a new line.

output1.png Demonstrating the running of a script

Using Variables

Variables are named storage locations. They are are used when you need to store a particular that you can use later. You can declare a variable and assign a value to it in Bash script. The format for declaring a variable is VARIABLE_NAME=value without spaces on both sides of the declaration. A variable name can be made up of uppercase letters, lowercase letters or a combination of both but remember that Bash is case sensitive. After declaring a variable, you refer to it using the dollar sign ($) followed by the variable name.

# Declare a variable NAME and assign a value to it.
MOOD=Excited!

# Display "I am Excited!"
echo I am $MOOD

I am sure you can guess what the above code would print, I am excited!, that's right! Note that you should use double quotes around complex variable values, like those that contain spaces as Bash uses space to separate items. For example MOOD=very excited! would return an error when the variable $MOOD is used but if we do this MOOD="very excited" would work as it should.

Command Substitution

You can assign the output of a command (What would have been printed on the screen) to a variable, this is known as command substitution. To do this, place it within brackets preceded by the $ sign.

#The code below will return the number of entries in a directory.
dirContents=$(ls | wc -l)
echo The number of entries in this directory is $dirContents

Special Variables

Special or positional variables are variables that are automatically set for you in Bash. Here are some of them:

  • $0 - This contains the name of the script.
  • $n - These contain command-line arguments with which a script was called. For example, $1 contains the first argument, $2, the second argument, and so on.
  • $# - This contains the number of command-line arguments given to the script
  • $* - All the command line arguments.
  • $? - The exit status of the last executed command or most recently run script. An exit status of 0 means success while 1 means failure.
  • $$ - The process ID of the current script

Basic Operators

In bash, you have access to arithmetic, relational, boolean, string and file test operators.

Arithmetic Operators

OperatorDescriptionUsage
+Additionnum1 + num2
-Subtractionnum1 - num2
*Multiplicationnum1 * num2
/Divisionnum1 / num2
%Modulusnum1 % num2
=Assignment$num = value

You can use arithmetic operators in bash scripts by using in a few ways, using the let or the expr keyword, using the square brackets or double bracket preceded by the $ sign. You can't just try to perform arithmetic operations by typing out the numbers with an arithmetic operator. Try typing 1 + 2 or 1+2 in the command line and you will see that it returns an error. If you try to echo them like so echo 1 + 2 or echo 1+2, they get treated as strings and will be displayed as 1 + 2 and 1+2 respectively. Study the code snippet below to understand how they are being used.

#!/bin/bash

#
let myVariable=2+3 #If you are declaring the expression without quotes, then you must ensure that the expression doesn't contain spaces.
echo $myVariable #This will output 5

#Using the let command
let "myVariable = 2 + 3" #You can have spaces within quotes.
echo $myVariable # This will output 5

let myVariable++ #Will increment the last value of $myVariable, so $myVariable will now be 6

#Using the expr command
expr 2 + 3 #You must include space when using the expr command and do not enclose the expression in quotes. This line will output the addition of the two numbers.

expr "2 + 3" #Will be treated as a string and will output 2 + 3
expr 2+3 #Will be treated as a string and will output 2+3

#Using brackets
echo $[2+3] #Will work with or without spaces and output the addition of the numbers.
echo $((2+3)) #Will also work with or without spaces.

Note - with the let command, you have to save the result to a variable while the expr command outputs the result.

Relational Operators

OperatorDescriptionUsage
-eqEqual to$num1 -eq $num2
-neNot equal to$num1 -ne $num2
-gtGreater than$num1 -gt $num2
-ltLess than$num1 -lt $num2
-geGreater than or equal to$num1 -ge $num2
-leLess than or equal to$num1 -le $num2

Note - these do not work for string values

Boolean

OperatorDescriptionUsage
!Negation!
-oLogical ORconditon1 -or condition2
-a (&&)Logical ANDcondition1 -a condition2

Note - You can also use || to represent Logical OR and && to represent Logical AND.

String Operators

OperatorDescriptionUsage
=Equal to; Will return true if the strings match$string1=string2
!=Not equal to; Will return true if the strings do not match$string1!=$string2
-zChecks if a given string operand size is zero (0).
Returns true if it is zero length
-z $string
-nChecks if given string operand size is non-zero-n $string

File Test Operators

Some Common test operators

OperatorDescriptionUsage
-dChecks if file is a directory-d $file
-fChecks if file is an ordinary file-f $file
-pChecks if file is a named pipe-p $file
-rChecks if file is readable-r $file
-wChecks if file is writable-w $file
-xChecks if file is executable-x $file
-sChecks if file has a size greater than zero (0)-s $file
-eChecks if file exists-e $file

Shell loops

Loops are used to execute a set of commands repeatedly.

For Loop

This type of loop repeats a set of commands for every item in a list.

for var in <list>
do
    <command>
done

Example

#!/bin/bash

#This will print numbers 1 to 5
for value in {1..5}
do
   echo $value
done

While Loop

This loop executes a set of commands while a given condition is true, so the loop will stop when the condition becomes false.

Syntax

while [ <condition> ]
do
    <command>
done

Example

#Will print numbers 1 to 5
count=1
while [ $count -le 5 ]
do
echo $count
((count++)) #Will increment count by 1 after every loop
done

Until Loop

This loop will execute a set of commands until a given condition becomes true.

until [ <condition> ]
do
    <command>
done

Example

#Will print numbers 1 to 5
count=1
until [ $count -gt 5 ] #Loop will stop one count is greater than 5
do
echo $count
((count++)) #Will increment count by 1 after every loop
done

Shell functions

A function is a reusable block of a set of commands. Functions help you to not repeat yourself as you can write a function to perform a specific task and then use it many times whenever and wherever you need it.

Function Syntax

#Without the function keyword
function_name () {
       <commands>
}

Or

#With the function keyword
function function_name {
       <commands>
}

Example

#!bin/bash
#A function that prints out the first argument supplied to it
 function printArg () {
          echo The first argument is $1
}
#Function call with an argument
printArg "Hello World"

#The output of the function above will be "The first argument is Hello World"

Both methods of declaring functions are valid and will behave the same so whichever you decide to use is based on your preference. If you are familiar with other programming languages that also have brackets in their function declaration, you would think the brackets are for arguments but in bash, it is not so. The brackets are merely for decoration and nothing should be placed in them. From the code above, you can see that arguments are provided directly after the function name when you call the function.
Note - you can't call a function before you have declared it.

Conditional Statements for Shell Scripts

Conditional statements allow you to make decisions based on a condition or test within your shell script.

If Statements

This will perform a given set of instructions or commands if the test or condition provided is true, the set of instructions won't be carried out otherwise.

Syntax

if [ <condition/test> ]
then
      <command>
fi

If Else Statements

This works just like the basic if statements explained above but also provides room for a condition to be performed if the condition is not true. If the condition is not true, the command or set of commands () between else and fi will be executed.

Syntax

if [ <condition/test> ]
then
      <command1>
else
      <command2>
fi

Example

#!bin/bash
#This script will take 2 numbers as command-line arguments. It will print to the screen the larger of the two numbers.

if [ $1 -gt $2 ]

then
   echo $1
else
   echo $2
fi

Output

image8.png

If Elif Else

This is used for multiple conditions with different outcomes.

Syntax

if [ <condition1> ]
then
      <command1>
elif [ <condition2> ]
then
      <command2>
elif [ <condition3> ]
then
      <command3>
else
      <command>
fi

Example A script that will print a message based upon which day of the week it is.

#!/bin/bash
#script to print a message based upon which day of the week it is.

day_of_the_week=$(date +%u)

if [ $day_of_the_week -eq 1 ]
then
   echo Today is Monday, aargh!
elif [ $day_of_the_week -eq 2 ]
then
   echo Today is Tuesday
elif [ $day_of_the_week -eq 3 ]
then
   echo Today is Wednesday
elif [ $day_of_the_week -eq 4 ]
then
   echo Today is Thursday
elif [ $day_of_the_week -eq 5 ]
then
   echo Today is Friday, TGIF!
elif [ $day_of_the_week -eq 6 ]
then
   echo Today is Saturday
elif [ $day_of_the_week -eq 7 ]
then
   echo Today is Sunday
else
   echo Something is wrong
fi

Output

image1.png
Note - you can change your system time to see the different outputs else it will only display the output for your present day.
You can have as many elif branches as you want. It is valid to have an if statement inside another one, that is nesting if statements. Note - You have to include spaces in the square bracket that contains the condition as seen in the examples else an error will be return.

Case Statements

This should be used in cases that might lead to a long string of if and elif statements, case statements present it in a much neater way.

Syntax

Case <variable> in
    <pattern1>)
          <command>
          ;;
    <pattern2>)
          <command2>
          ;;
esac

Example

#!/bin/bash

echo Which color do you prefer? Blue, Green or Yellow? #This will be printed first when you run the script.
read color #Prompt to accept input

case $color in #Compare using the value entered for color at the prompt********
    Blue)
        echo Feeling blue today?
        ;;
    Green)
        echo The land is green!
        ;;
    Yellow)
        echo It is going to be a sunny day!
        ;;
     *)
        echo Try Again
        ;;
esac #Indicates the end of the case statement

Output

case.png

Conclusion

Bash/Shell Scripting is vast and this article is just to help understand the basics to get started. With your knowledge of Linux commands, you can do really useful things with bash scripting and automate repetitive tasks.
Thank you for reading, I hope you find this guide useful 🙂.

Resources

Comments (2)

ToyinO's photo

Thank you for the plug 😁 Your article is so well written.