Skip to main content
Skip to main content

tldr

This document is a brief overview how to structure your bash scripts. To get a more detailed explanation (e.g. arrays, subprocesses, etc.), please read the Styleguide.

Shebang

The first line of your script should be the shebang. This is the line that tells the system what interpreter to use to run the script. Always use #!/usr/bin/env bash instead of #!/bin/bash or #!/usr/bin/bash because it's more portable.

#!/usr/bin/env bash

Strict mode

The strict mode is a set of options that make the script more robust and less error-prone. It's a good practice to always use it.

set -euo pipefail

Environment variables

If you have any environment variables that your script depends on, define/show them at the top of the script.

: "${LOG_LEVEL:="info"}"
: "${LOG_ENABLED}"

Use := to provide a default value for the variable. If don't want that the variable can be set by the user, and is only used internally, declare them directly. If these are imutable, use readonly to make them readonly.

LOG_LEVEL="info"
readonly LOG_LEVEL

::: note

If you want to use the LOG_LEVEL in sub processes, you have to export it with export LOG_LEVEL.

:::

Constants

If you have any constants, define them at the top of the script.

readonly path="${BASH_SOURCE[0]%/*}"

Sourcing

If you have any functions or other scripts that you want to use in your script, source them at the top of the script.

source "${path}/functions.sh"

Functions

Define your functions after the constants and sourcing. This makes it easier to find the main part of the script.

hello_world() {
local name="${1:-world}"
echo "Hello, ${name}!"
}

Declare your function parameters with local to make them local to the function. You may use -r to make them readonly. Use :- to provide a default value for the parameter.

Namespacing

If you have a lot of functions, you can namespace them by using a prefix.

to::boolean() {
local -r value="${1}"
case "${value}" in
false|no|0) return 1 ;;
* ) return 0 ;;
esac
}

main() {
if to::boolean "${LOG_ENABLED}"; then
echo "Logging is enabled"
else
echo "Logging is disabled"
fi
}

Variables and name conventions

Use lowercase for variable names. Use _ to separate words. Enclose the variable name in ${} to make it clear where the variable name starts and ends. Additionally, this makes it easier to use Bash expansions.

External commands

Try to avoid external commands like grep, awk, sed, etc. Use Bash builtins instead, fall back to coreutils if necessary. If you are using external commands, make sure to check if they are available or provide them (e.g. with the binary library).

if ! command -v jq &> /dev/null; then
echo "jq could not be found"
exit 1
fi

Test expressions

Use [[ instead of [ for test expressions. It's more powerful and has less surprises. Use arithmetic expressions (( for arithmetic operations/tests.

local check=1

(( check == 1 )) || {
echo "Check failed"
exit 1
}
Note

If you use assert like checks, make sure to use || instead of && to make the script not fail if the check fails. [[ <expr to be true or ...> ]] || { echo "do something else"; }

Main

The main part of the script should be at the bottom of the script. This is where you call the functions and do the work.

main() {
echo "Hello, world!"
}

Call main

Call the main function at the bottom of the script. Check if the script is being run directly or sourced.

[[ "${BASH_SOURCE[0]}" != "${0}" ]] || main "${@}"
Was this section helpful?