SHELL SCRIPTING FOR BEGINNERS
Tags: shell, scripting, beginners, bash
Published: 2023-01-25 12:07:47

A Shell script is a program designed to be run on the comand-line-interpreter (CLI). With shell scripting, you can write a series of commands in a file and then once you run the file, each command will be executed by the shell automatically. This allows you to perform repetitive tasks more efficiently. Additionally, there is a wide range of use cases of shell scripting, such as installing and running programs, creating a program environment, scheduling data backups, and many more.

### WHAT IS THE SHELL

A shell is a program that acts as an interface between the user and the kernel or, more simply put, it translates the commands from the terminal to the operating system. The original unix shell (`sh`) was written by Steve Bourne. At present, the most popular shell is `bash`, which stands for Bourne Again Shell and is an enhanced version of the original shell. Other shell programs include but are not limited to: `zsh` (Z Shell), `csh` (C Shell) and `ksch` (KornShell).

### BASH SHELL FILE IDENTIFICATION

Shell scripts use the `.sh` extension and begin with a shebang ( e.g. `#! /bin/bash`, `#! usr/bin/bash`), which indicates to the shell how to locate the interpreter required to execute the script. In its essence, the shebang is a special kind of comment which contains the absolute path to the interpreter and it varies for different computer systems. You can check which is the correct shebang on your machine by running the command `which bash` in your terminal.

### SCRIPT EXECUTION RIGHTS

Let's create an example file:

```bash
touch example.sh
```

By default, after creation, the file has only read and write permissions, represented by "r" and "w". The execution right is marked with an "x". You can learn more about file permissions in this article on [guru99](https://www.guru99.com/file-permissions.html).

In brief, you can check the permissions of a file by runinng `ls -l <filename>`

```sh
ls -l example.sh
-rw-r--r--@ 1 user  0 20 Jan 15:37 example.sh
```

"-rw-r--r--": The first three letters represent the rights of the owner of the file. Rights which are not granted are marked with a dash instead of their respecive letters. In this case the owner has read and write rights (`rw-`), while the user and group rights are read-only (`r--`).
In order to allow the execution of the `example.sh` file for all:

```sh
chmod +x example.sh
-rwxr-xr-x@ 1 user  0 20 Jan 15:37 example.sh
```

"-rwxr-xr-x": You can see that now the owner has all three rights (`rwx`), while the user and group have read and execute rights (`r-x`).

### WRITING SHELL SCRIPTS

Shell scripts use the same syntax you would use on the shell command line.
Here is a list of some of the essential Linux commands posted on [Digital Ocean](https://www.digitalocean.com/community/tutorials/linux-commands).

Let's keep our example simple and print the message "Hello World" to the console:

```sh
#! /bin/bash
echo "Hello World"
```

### Executing the shell script

To execute the script, use the command `bash <filepath>`. In my case, the file is located in the current folder so:

```sh
bash example.sh
```

### Defining variables

Defining variables in a shell script is as easy as `variable_name=variable_value`.

```sh
#! /bin/bash

name="John"
surname="Doe"

echo "Hello, my name is $name $surname"
```

### Using arguments

Arguments are passed after the filename in the form of a sequence of strings (or numbers) separated by space. In our example, we pass two arguments to the `example.sh` - John and Doe:

```sh
bash example.sh John Doe
```

In the script, the arguments are identified by the order in which they are received, starting with 1.
Hence, `name=$1` will equal `name="John"` while `surname=$2` will equal `surname="Doe"`

```sh
#! /bin/bash

name=$1
surname=$2

echo "Hello, my name is $name $surname"
```

### Setting default values for the variables

Default values could be set for the variables in the script in case that no argument is received.
The syntax is `variable_name=${arg_position:-"Default Value"}`. For example,

```sh
#! /bin/bash

name=${1:-"John"}
surname=${2:-"Doe"}

echo "Hello, my name is $name $surname"
```

### Reading user input

User input can be saved inside a variable with `read variable_name` or, if you would like to prompt the user with a specific message, you can add the -p flag and the message - `read -p "Propmt Message: " variable_name`. You can see both options in the following example:

```sh
#! /bin/bash

echo "What is your name?"
read name

read -p "What is your surname?" surname

echo "Nice to meet you, $name $surname."
```

### Setting dynamic variables

In case you want to calculate the value of a variable during runtime, you could use the `$(( expression ))` syntax. For example, the user provides two numbers and you want to evaluate their sum:

```sh
#! /bin/bash

echo "Enter a number"
read x

echo "Enter another number"
read y

sum=$((x+y))

echo "The sum of $x and $y is $sum"
```

### Conditional statements

The keywords used to write conditional statements in bash are:

- **if [ logical operation ]**: represents the condition that must be evaluated before proceeding
- **then**: indicates that the following commands must be executed when the if-condition (or elif-condition) is satisfied
- **elif [ logical operation ]**: in case that the if-condition was not satisfied, evaluate the logical operation in the brackets before proceeding
- **else**: if neither of the if- or elif-conditions were satisfied, evaluate the following commands
- **fi**: end of the conditional statement

The general syntax looks like this:

```sh
if [ condition ]
    then
         ...commands
elif [ condition ]
    then
        ...commands
else
    ...commands
fi
```

**Note the space surrounding each bracket**

Let's look at the following example. The user is prompted to enter two numbers and the script must find out which of the numbers is bigger. If the difference between the first and second number is negative, then the second number must be bigger, else the first one must be the bigger one. If it is 0, then the numbers are equal.

```sh
#! /bin/bash

echo "Enter a number"
read x

echo "Enter another number"
read y

dif=$((x-y))

if [ $dif -gt 0 ]
    then
        echo "$x is bigger than $y"
elif [ $dif -lt 0 ]
    then
        echo "$x is smaller than $y"
else
    echo "$x is equal to $y"
fi
```

It is important to note that evaluating multiple logical operations in a single condition requires the use of double brackets `[[ logical_op1 && logical_op2 ]]`. Upgrading the previous example to check if the first number is positive and determine if it is bigger than the other:

```sh
#! /bin/bash

echo "Enter a number"
read x

echo "Enter another number"
read y

if [[ $x -gt 0  &&  $x -gt $y ]]
    then
        echo "$x is positive and bigger than $y"
elif [[ $x -gt 0  && $x -lt $y ]]
    then
        echo "$x is positive, but smaller than $y"
else
        echo "$x is negative"
fi
```

**Note the spacing around the brackets**

You can find a cheat sheet with the logical operators in bash provided by [kapeli.com](https://kapeli.com/cheat_sheets/Bash_Test_Operators.docset/Contents/Resources/Documents/index)

### For-Loops

For-loops are used to repeat a block of code several times. It is important that the number of repetitions must be known; it might be known only by the computer, not by you, but it is still known.
The general syntax of a for-loop is:

```sh

for variable_name in list_of_values
do
    commands
done

```

```sh
#!/bin/bash

for city in London Paris Berlin
do
	echo "$city"
done
```

When looping through numerical values, you can set the loop's boundary conditions as an interval defined with curly brackets and separated by two dots , i.e. `{start_value..end_value}`. For example,

```sh
#!/bin/bash

for i in {1..5}
do
    echo $i
done
```

A cool use-case for the for-loop is looping through all the arguments when you don't know how many they are.
I hope you remember that an argument is read by the script with `$` appended with the order in which it is received (i.e `$1`, `$2`). You can substitute the order number of the argument with an `@` to capture all available arguments ( i.e. $@ = `arg1 arg2 arg3`). Then if we run the script below with arguments - London, Paris and Berlin, the output will match the output of the cities example above.

```sh
#!/bin/bash

for x in $@
do
    echo "$x"
done
```

### While-Loops

For-loops are great but they are only useful when the number of repetitions (loops) that must be performed is known. When that number is unknown, you have to use a while-loop. It repeats a block of code as long as some condition is satisfied. The general syntax of a while-loop is:

```sh
while [ condition ] ; do
    commands
done
```

For example, say we want to count from 1 to a given number, which is entered by the user:

```sh
#!/bin/bash

read -p "Count to: " count_to

i=1
while [ $i -le $count_to ] ; do
   echo "$i"
  (( i += 1 ))
done

```

The script prompts the user to enter a number and starts counting from 1. While the current count number (`i`) is less than the user input (`count_to`), the loop will print the current count, add 1 to it and start over. When the current count (`i`) becomes bigger than the user input, the condition will no longer be satisfied and the code won't run the while-loop.

### CONCLUSION

In this article we used examples to show some of the main tools used in shell scripting. Starting with the execution of the shell script file, going through using variables and arguments, up to writing conditional statements and loops. However, there is a lot more to shell scripting and if you are interested in learning more about it, here is an in-depth guide by [Mendel Cooper](https://tldp.org/LDP/abs/html/) and a comprehensive cheat sheet on [devhints](https://devhints.io/bash).