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