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.
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. Theset -eoption 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, useset -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.
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.