thermondo

thermondo Developer Handbook

View My GitHub Profile

Shell Scripting

ShellCheck

If you take one thing away from this page, it should be ShellCheck.

Don’t write shell scripts without it. This applies to even trivial scripts.

ShellCheck will not only save you from common mistakes; it is also arguably the most valuable tool to improve your understanding of shell scripting in general. This is because every ShellCheck warning can be looked up in the ShellCheck wiki, which explains what the warning means, why your code is (probably) problematic, and shows better ways to solve your problem.

Virtually every text editor comes with ShellCheck support or has a ShellCheck plugin. It also exists on GitHub Actions runners, and should be used in CI. Example:

jobs:
  lint:
    runs-on: ubuntu-22.04
    steps:
      - uses: actions/checkout@v4

      - name: ShellCheck
        run: "shellcheck *.sh"

All shell scripts should either be lint-free, or have # shellcheck disable=XXXXX comments to ignore specific warnings. Each of these comments should come with an explanation of what it is you’re ignoring and why. For example:

# shellcheck disable=SC2016  # $MONEY$ will be printed verbatim with dollar signs
echo 'show me the $MONEY$'

Linux / macOS compatibility

For scripts that are designed to run exclusively on servers, you don’t need to worry as much about compatibility with macOS. However if scripts will be used (or tested) by macOS developers, then you do need to make sure your scripts are compatible with both macOS and Linux.

Linux machines tend to come with recent versions of bash. macOS is still on bash 3. If you are a Linux developer, it is especially important to test your bash scripts on a mac (or get a mac developer to help you test your scripts).

Linux and macOS commands also have slight differences in syntax. If you stick to POSIX commands, that will reduce the effort required to keep your scripts cross-platform. For example, always use mkdir -p instead of mkdir --parent.

Shebangs

Shebangs should look like this:

#!/usr/bin/env bash

bash is normally what our scripts target, because it’s everywhere. sh is even better if you don’t need any bash-isms. zsh, fish, etc. shouldn’t be used, because they are not widely used enough by both Linux and macOS machines.