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.
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
localvariables and callmain "$@"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:?}"/*.
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?
Formats scriptsLints shell scripts and points out bugs and risky patternsRuns scripts in a sandboxConverts Bash to Python
Show answer
Answer: Lints shell scripts and points out bugs and risky patterns