/ BASH

Working with Bash

Gnu-bash-logo.svg

Overview

As Linux has basically taken over the cloud, and servers in general, leaving Windows Servers to look after Active Directory and SQLServer, knowing Bash is a must. This artical isn’t a Bash introduction, its an introduction to creating shorter more efficient scripts.

One note to take here is that the examples are Bash 4.x specific and have only been tested on Linux. Some examples may not work with the version of Bash that is shipped with MacOS.

Making Conditions a Little Shorter

You are bound to make use of if-then-else statements within your Bash scripts, but sometimes it make sense to shorten these. For example:

if [ $(grep root /etc/passwd) ]; then
	echo "Root exists"
fi

The above will work perfectly fine, however it can be easily shortened.

[ $(grep root /etc/passwd) ] && echo "Root exists"

This works because [ is an actual command on Linux/Unix, which is equivilant to the test command. Both of these utilities evaluate the expression and, if it evaluates to true, returns a 0 exit status, otherwise it returns 1. So, for the above example, hopefully, this would return a 0.

OK, so what is &&? This is an AND operator, so the above would read; if command return a 0 echo “Root exists”. ||, which is an logical OR operator, is used if you want to take an action for false. Now we can make this the above example a little more useful by adding an operation to perform should false be returned.

[ $(grep root /etc/passwd) ] && echo "Root exists" || echo "Root does not exist"

Now that is more useful. But maybe you want to perform 2 or more commands for each operation. This is performed by grouping commands together within curely braces as follows.

[ $(grep root /etc/passwd) ] && echo "Root exists" || { echo "Root does not exist"; exit 1; }

As always with Bash note the spaces afer the opening brace and before the closing brace. Also note that you must have a semi-colon after each command.

You could go mad and have extremely long commands grouped like this, but you are loosing the elegance and should think about reverting back to the standard if-then-else statement at some point.

Functions

Functions in Bash provide a method to reuse code. A function can be considered as a small script within a script. It’s a small chunk of code which you may call multiple times within your script.

Creating a function is straight forward

function() {
	code
}

In other languages it is common to have arguments passed to the function listed inside the brackets (). However, in Bash they are there only for decoration and you never put anything inside them. Technically the brackets are not required, but for readability I recommend leaving them in place.

Note that a function definition has to appear in the script before any calls to the function. It is safest to place all your functions at the top of your script.

Passing Arguments to a Function

Functions can reference any variable within the script, but you can also pass arguments. You supply arguments after the function name. Within the function they are referenced as $1, $2 and so on.

#!/bin/bash

example () {
        echo Hello $1
}

example Jon
example Dominic

Variable Scope

As mentioned any variable outside of a function can be referenced, as variables default to being global in scope. Variables within a function can be limited in scrope to the function using the local keyword.

localvar () {
	local var='local'
}

Verifying Exit Status Using a Function

Its obviously extremely common to run a command and verify that the exit status of this command.

apt update
if [ $? -eq 1 ];then
	echo "apt failed"
	exit 1
fi

This example will work fine, but will get annoying if you constantly have to repeat the above. There must be a better way? Luckily there is, by taking advantage of Bash functions.

function exitStatus {
        "$@"
        local status=$?
        if [ ${status} -ne 0 ]; then
                echo "error with $1" >&2
                exit ${status}
        fi
        return ${status}
}

exitStatus apt update
exitStatus apt -y install openjdk-11-jre unzip git docker.io

Summary

Hopefully the above has expanded your knowledge of Bash’s capabilities to some degree, and will help you in the future.