Bash Tutorial · Writing Good Scripts · Lesson 29 of 30

Bash Script Style and ShellCheck

Write maintainable scripts: a standard template, naming, quoting, and linting with ShellCheck.

Create a free account to save this page, take notes and track your progress. Sign in

A script template

#!/usr/bin/env bash
# Rotates application logs older than N days.
set -euo pipefail

readonly LOG_DIR="${LOG_DIR:-/var/log/app}"

main() {
  local days="${1:-7}"
  find "$LOG_DIR" -name '*.log' -mtime +"$days" -print -delete
}

main "$@"

Style rules that prevent bugs

  • Quote every expansion: "$var", "$(cmd)", "${arr[@]}".
  • Use [[ ]] instead of [ ], and $(...) instead of backticks.
  • Put logic in functions with local variables and call main "$@" at the end.
  • Prefer long options in scripts (--recursive) for readability.

ShellCheck

ShellCheck finds bugs such as unquoted variables, useless cat, and cd without error handling. Run it in your editor and in CI: shellcheck scripts/*.sh. For example, it flags rm -rf $dir/* (if dir is empty this becomes rm -rf /*) and suggests rm -rf "${dir:?}"/*.

Protecting destructive commands
dir=""
# ${dir:?} stops the command instead of expanding to an empty string
rm -rf "${dir:?}"/tmp 2>&1 | sed 's/^.*: line [0-9]*: //' || true
echo "nothing was deleted"

Output

nothing was deleted

Run this on your computer — see “Get Started” for setup.

When to switch languages

Bash is great for gluing commands together. When a script needs data structures, JSON handling, or grows past a couple of hundred lines, Python or Go is usually easier to test and maintain.

Test yourself

What does ShellCheck do?

  1. Formats scripts
  2. Lints shell scripts and points out bugs and risky patterns
  3. Runs scripts in a sandbox
  4. Converts Bash to Python
Show answer

Answer: Lints shell scripts and points out bugs and risky patterns

Was this page helpful?