Computer scienceSystem administration and DevOpsCommand lineBash syntax

Best practices for bash scripts

7 minutes read

Bash helps you access our operating system more efficiently, allows you to automate monotonous tasks, and makes working with files easy. Overall, it makes our lives easier. As bash script files contain lots of commands, they should be consistent, easy to read and maintain, and efficient. Let's look at some ways which can help you achieve your goal.

Script example

Here is a simple bash script that provides functionality for addition and subtraction.

#!/usr/bin/env bash
set -euo pipefail

function add() {
    echo $(( $1 + $2 ))
}

function subtract() {
    echo $(( $1 - $2 ))
}

case $1 in
    add )
        add $2 $3;;
    subtract )
        subtract $2 $3;;
    * )
        echo "unknown operation";;
esac

To understand how to organize your script, let's divide the file into multiple parts:

Shebang

In a broader sense, a shebang is a character sequence that starts with #!. As you might have noticed, the shebang #!/usr/bin/env bash resembles a path. This is a path to the bash interpreter that is used to execute the bash script. If a shebang is not specified, the default interpreter for the particular file extension is used. However, when a shebang is used, the specified interpreter will execute the script.

Let's dive deeper into this with an example of how this works. By default, different scripts with different extensions are executed using different interpreters. A .sh script will be executed using the sh interpreter, whereas a .bash script will be executed using the bash interpreter. Bash is a superset of sh and provides some additional features. When you write a script file to execute it with the bash interpreter, you will have to specify the shebang for the bash interpreter.

Don't forget that the shebang must always be the first line in the script. Otherwise, it will be interpreted as a comment.

Error handling

Working with a bash script is somewhat different than working with the bash shell itself. When you create script files, you have a clear goal. Since we do not execute each command manually, it is important to handle errors on the whole script properly. To do this, use the line: set -euo pipefail. It is made up of 3 parts:

  • set -e: by default, bash does not halt the execution of the script. This is helpful when we work with bash shell but not when executing bash scripts. The set -e option exits bash immediately when faced with an error.
  • set -u: this will cause the program to automatically exit when an undefined variable is referenced.
  • set -o pipefail : When a command in a pipeline fails, it returns the code of the last executed command. To return the command that was the reason behind the failure, use set -o pipefail.

We can use these three commands separately. But combining them makes things much more convenient.

Case statement

The bash case statement is a type of conditional statement. Case statements are more readable and also easier to maintain. It checks whether a value matches another predefined value and executes commands linked to the latter. Below is a simple example of a case statement in bash.

case $variable in
    condition-1 )
        commands;;
    condition-2 )
        commands;;
    condition-3 )
        commands;;
    condition-N )
        commands;;
    * )
        commands;;
esac

The statement begins with case, and then you specify a value that will determine which condition to work with. After that, write in and then specify our conditions. The end of each condition is marked with ) after which its corresponding commands are written. The commands end with ;;. After specifying all our conditions we may add a default case. This is done by using *. We can end our case statement by writing the case command backward — esac.

Functions

Functions in bash are reusable pieces of code that you can call multiple times in the script. This helps you maintain the script easily. Let's look at an implementation of a function in bash:

function function_name_multiplier () {
    echo $(( $1 * $2 ))
}

A closer look will reveal something peculiar in our function. $1 and $2 are the references to values that are provided as input to the function in order. Use function_name_multiplier 2 5 to execute the function; the output is 10.

ShellCheck

When working with programming languages like Java, Python, and JavaScript, the best practice is to use code editors and IDEs. They also help us find errors and provide suggestions. Luckily, there's a similar program for working with bash scripts — ShellCheck. As stated on its GitHub page, it is a GPLv3 tool that gives warnings and suggestions for bash/sh shell scripts. Let's take a look at the tool in action.

ShellCheck with load example and output

Conclusion

In this topic, we took a look at some practices for writing bash scripts that will help us write better scripts;

  • Start the script with a shebang that specifies the interpreter.
  • To handle errors write the line set -euo pipefail.
  • Try to parse arguments with case statements.
  • Describe each command in a separate function.
  • Use ShellCheck to check that our code follows best practices.
78 learners liked this piece of theory. 1 didn't like it. What about you?
Report a typo