An Overview of Bash Shell Scripting for Automating Tasks

Shell scripting is a powerful way to automate tasks that you frequently perform on your computer. In this tutorial, we will provide a comprehensive overview of shell scripting, which will serve as the foundation for more advanced tutorials on creating practical shell scripts.

Explore our introduction to Bash post to learn more.

Bash provides a set of commands that can be combined to create small programs known as scripts. It’s important to note that we refer to this process as Bash scripting, not programming, and the scripts themselves as Bash scripts, not programs. This distinction is made because Bash scripts can become complex, but they still rely solely on the Bash shell to run. No additional compilers or interpreters are required.

While Bash lacks features found in other programming languages like Perl, JavaScript, or Python, such as scoped variables, standard libraries, or module systems, it offers its own advantages. One of the most significant advantages is the ability to easily invoke command-line tools and leverage the numerous utility commands available in Unix. For example, you can perform network requests with wget or process text using awk.

Shell scripting is a tool that you should be familiar with, at least in terms of understanding how to read a script and the benefits it can bring to your daily work. This tutorial will guide you through the theory and concepts of Bash scripting, with more detailed tutorials on specific techniques and problem-solving to come in the future.

Basics

Scripts are stored in files, and you can give them any name and extension. The crucial requirement is that they must start with a “shebang” on the first line:

#!/bin/bash

Additionally, the file must be executable, which can be achieved using the chmod utility command. For example:

chmod u+x myscript

This command makes the myscript file executable for your user. Once executable, you can execute the script by calling it either ./myscript if you’re in the same folder, or by using the full path to the script if you’re in a different location.

During your learning process, it’s recommended to use an online playground, such as this one, to make testing and experimentation easier.

Comments

Comments are essential when writing programs, and in Bash, a line starting with the # symbol is considered a comment (with the exception of the shebang line). For example:

#!/bin/bash

Comments can also be placed at the end of a line:

#!/bin/bash
echo ok # This is a comment

Variables

Variables in Bash are set using the = operator. For example:

name=value

Here are some examples:

ROOM_NUMBER=237
name=Flavio
nickname="Flavio"

To print the value of a variable, use the echo built-in command, adding a $ before the variable name:

echo $ROOM_NUMBER
echo $name

Operators

Bash supports various arithmetic and comparison operators. For arithmetic operations, you can use +, -, *, /, %, and ** for addition, subtraction, multiplication, division, modulo, and exponentiation, respectively.

Comparison operators include <, <=, ==, >=, and >. You can also use the following:

  • -lt: lower than
  • -gt: greater than
  • -le: lower or equal than
  • -ge: greater or equal than
  • -eq: equal to
  • -ne: not equal to

Here’s an example using the comparison operator -lt:

#!/bin/bash
age=23
minimum=18
if test $age -lt $minimum
then
 echo "Not old enough"
fi

Bash also includes logical operators && for AND and || for OR. Additionally, shortcuts such as +=, -=, *=, /=, and %= are available for performing arithmetic operations and assignment together.

While there are more operators in Bash, these are the most commonly used ones.

Print to the Screen

To display output on the screen in Bash, use the echo command. For example:

#!/bin/bash
echo "test"
echo test
echo testing something

Logical Conditions

Bash provides logical conditions using the && operator for AND and the || operator for OR.

For AND conditions, use the following syntax: command && anothercommand.

For OR conditions, use: command || anothercommand.

To invert the logical return value of a command, use the ! symbol before the command: ! command.

Control Structures

Bash supports several control structures that you might already be familiar with.

If/Else Statements

Use the if statement for simple if conditions:

if condition
then
 command
fi

For if-else statements, use:

if condition
then
 command
else
 anothercommand
fi

Nested if-else statements can be written as:

if condition
then
 command
elif
 anothercommand
else
 yetanothercommand
fi

You can also use a semicolon to keep the else statement on the same line:

if condition ; then
 command
fi

Here’s an example illustrating the usage of if statements:

#!/bin/bash
DOGNAME=Roger
if [ "$DOGNAME" == "" ]; then
 echo "Not a valid dog name!"
else
 echo "Your dog's name is $DOGNAME"
fi

Notice the use of brackets when performing boolean evaluations.

Loops

Bash provides several looping mechanisms, including while, until, for in, break, and continue.

While Loop

The while loop runs a command as long as a certain condition remains true:

while condition
do
 command
done

Until Loop

The until loop runs a command until a certain condition becomes true:

until condition
do
 command
done

For Loop

The for in loop allows you to iterate over a list of values and execute a command for each item:

for item in list
do
 command
done

Break and Continue

Inside loops, you can use the break and continue statements to exit the loop completely or skip the current iteration, respectively.

Case Statements

Case statements allow you to choose different paths based on specific values. The syntax is as follows:

case value in
 a)
 command
 #...
 ;;
 b)
 command
 #...
 ;;
esac

The *) case handles all cases not explicitly expressed. You can also use the | symbol to add multiple choices for a single case.

Here’s an example illustrating the usage of case statements:

#!/bin/bash
read -p "How many shoes do you have?" value
case $value in
 0|1)
 echo "Not enough shoes! You can't walk"
 ;;
 2)
 echo "Awesome! Go walk!"
 #...
 ;;
 *)
 echo "You have more shoes than you need"
 #...
 ;;
esac

Select Statements

The select statement provides a menu of choices for the user to select from:

select item in list
do
 command
done

Here’s an example illustrating the usage of select statements:

#!/bin/bash
select breed in husky setter "border collie" chiwawa STOP
do
 if [ "$breed" == "" ]; then
 echo Please choose one;
 else
 break
 fi
done

echo "You chose $breed"

Testing Conditions

The test command in Bash allows you to check conditions and return a true (0) or false (non-0) value.

Here’s an example illustrating the usage of the test command:

#!/bin/bash
if test "apples" == "apples"
then
 echo Apples are apples
fi

if ! test "apples" == "oranges"
then
 echo Apples are not oranges
fi

Reading Input from the Command Line

To make your scripts interactive, you can use the read built-in command. This command reads a line from the standard input and can be formatted in various ways.

The simplest usage is as follows:

echo "Age:"
read age

In this example, “Age:” will be printed, and you can enter a number which will be assigned to the age variable.

The -p option of read provides a built-in prompt and assigns the input to a variable:

read -p "Age: " age

The read command has additional options, which you can explore by typing help read in the Bash terminal.

Adding Options

Options in Bash scripts are specified using a hyphen followed by a letter, such as -a or -b. You can use the getopts built-in command to parse the options and retrieve their values.

To accept options a and b, use getopts ab in a while loop. If an option requires a value, append a : after it. For example, getopts ab: arg.

Here’s an example illustrating the usage of getopts:

while getopts ab: arg
do
 case $arg in
 a) echo "Option $arg enabled" ;;
 b) echo "The value of $arg is $OPTARG" ;;
 esac
done

Within this script, $OPTARG is automatically assigned the option letter. By specifying a colon : before the argument definition (e.g., getopts :xy: arg), you can handle error messages manually. This allows you to control error messages for missing or invalid options.

Working with Parameters

In Bash scripts, you can access parameters passed at invocation time using special variables. $0 refers to the name of the command, while $1, $2, etc. represent the positional parameters passed to the script. After the 9th position, you need to use curly braces: ${10}, ${11}, and so on.

For example, running the following script:

#!/bin/bash
echo $0
echo $1

As ./startCar fiesta, will output:

./startCar
fiesta

The special syntax $* prints all parameters, and $# denotes the total number of parameters passed. For example:

#!/bin/bash
echo $# $\*

When executing ./test.sh Milan Florence Rome, the output will be:

3 Milan Florence Rome

Separating Options from Parameters

If your script includes both options and parameters, it is important to separate them. Use a hyphen to distinguish options from parameters. For example:

driveCar -m "Ford Fiesta" - Milan Florence Rome

Working with Strings

To determine the length of a string, use ${#string}. When working with strings in Bash, always use quotes to prevent special characters from being interpreted.

String comparison can be done using the = or == operators. For example:

"$dogname" = "$anotherdogname"
"$dogname" == "$anotherdogname"

To check for inequality, use !=.

Arrays

Arrays in Bash are declared inside parentheses, and their elements can be accessed using square brackets. To get the total number of items in an array, use ${#array[@]}.

Built-in Commands

Bash provides various built-in commands, such as test, read, and echo. To see a list of all built-in commands, use the help command, followed by the desired command (e.g., help read).