Linux shell scripting for beginners
Tags: linux, bash, beginners, webdev
Published: 2023-11-06 14:21:10

As a full stack or backend developer, you will inevitably need to write some BASH; either to deploy or manage some aspect of your web apps. We are used to high-level languages such as TypeScript, Python, PHP, and so forth. When it comes to writing BASH, it can be such a pain to even do basic tasks.

In this guide - I will cover some of the common concepts in the bash scripting language that will hopefully make your Linux scripting a little less painful.

NOTE: these should work on BASH shells you commonly find on distro's such as Ubuntu, however not all statements are strictly POSIX complaint. Learn more about [POSIX here](https://en.wikipedia.org/wiki/POSIX).

1) IF statements
```bash
x=1

if ((x==2)); then
  echo "$x = 2"
fi

if ((x>0)); then
  echo "$x > 0"
fi

if ((x<=1)); then
  echo "$x <= 1"
fi

```
The above is an IF statement that performs arithmetic comparisons. We use "(())" double round brackets to ensure that the parser treats this as a mathematical comparison. 

One problem though with this approach; they don't play nice with floating point numbers. You will get a weird error similar to:

> test.sh: line 2: [: 1.50: integer expression expected

Luckily, BASH comes with a handy library "bc" - that can handle more advanced math. Here is an example below:

```bash
if (( $(echo "$x < 3.25" | bc -l) )); then
  echo "$x is less than 3.25"
fi
```
This command will essentially return 1 or 0, and then we use the regular arithmetic IF to test if this is true i.e. "1".
```bash
$(echo "$x < 3.25" | bc -l)
``` 

The "-l" option we pass to bc basically just "imports" all the essential math functions needed.

For comparing strings - you can use square brackets. Actually, you can also use square brackets to compare numbers as well:

```bash
if [ "hello world" = "world" ]; then
    echo "Yes, this is true."
else
    echo "No, this is false."
fi

if [ "$a" -gt "$b" ]; then
     echo "Yes $a is greater than $b."
fi

```

2) Lists

To declare a list in BASH:

```bash
countries=("South Africa" "Japan" "China" "United States")
```
One thing that can trip you up, is the delimiter between each item. Normally in most languages, we use a "," to separate items however in bash it's just a space.

To loop through the list:
```bash
countries=("South Africa" "Japan" "China" "United States")
for country in "${countries[@]}"; do
  echo $country
done
```
When looping through lists, be careful not to forget the quotes in the following line:

```bash
for country in "${countries[@]}"; do
``` 
Leaving out the quotes will incorrectly interpret all spaces and print the following:
```text
South
Africa
Japan
China
United
States
```

3) Maps

```bash
declare -A profile
profile[name]="Kevin"
profile[country]="South Africa"
profile[website]="https://plexscriptables.com"

echo  "Name: ${profile[name]} , Website: ${profile[website]}"
```
You can also loop through the map:

```bash
declare -A profile
profile[name]="Kevin"
profile[country]="South Africa"
profile[website]="https://plexscriptables.com"


for key in "${!profile[@]}"; do
    echo "$key : ${profile[$key]}"
done
```

4) Functions

```bash
gotoServer () {
   local user=root
   ssh $user@$1
}

gotoServer 192.168.1.1
```
A few important points to note when using functions:

- They can access variables from outside the function.
- use "local" in front of the variable name to limit the scope of the variable to your function.
- Functions don't expect arguments to be defined as with other languages. Anything after the function name when called will automatically be available in positional variables: $1,$2,$3, and so forth.

5) Running DB queries (MySQL)

```bash
sql_query="SELECT TABLE_NAME,DATA_LENGTH FROM  TABLES;"
result=$(
    mysql -ukevin -p1234 information_schema -e "$sql_query"
)

IFS=$'\n'
for row in $result; do
  IFS=$'\t' read -a cols <<< "$row"
  echo "Table Name: ${cols[0]}, Data Length: ${cols[1]}"
done
```

In the about we are effectively fetching rows from the database and printing them out.

You will notice we set a variable before the for loop:
```bash
IFS=$'\n'
```
The MySQL command will essentially return text. IFS (internal field separator) will cause the loop to read the MySQL output line by line. This is similar to the "split" or "explode" functions in other languages.


```bash
IFS=$'\t' read -a cols <<< "$row"
```

In the above: **"read -a cols"** - is similar to a loop, it'll split each row by the tab character and the **"-a"** flag basically creates a variable with a type of "array".

## Conclusion

BASH is weird, its syntax is overly cryptic. I guess this is so because of its origins being in the 80s before all the cool modern tooling was invented.

Nonetheless, it's a powerful tool in your arsenal, to quickly run short scripts and, will make you super productive in your terminals, without needing to write a full script in Python or Perl.