Static Analysis with ShellCheck
Shell scripts fail in recurring ways. ShellCheck encodes many of those failure patterns as fast static diagnostics that can run before a test, deployment, or incident reaches the problematic line.
Learning objectives
By the end of this lesson
- Run ShellCheck for the correct shell.
- Interpret rule codes and severities.
- Fix quoting and expansion problems.
- Document rare suppressions.
- Enforce analysis consistently in CI.
1. ShellCheck finds shell-specific bugs without executing code
ShellCheck parses shell source and applies rules for quoting, expansion, tests, command substitution, sourcing, portability, and many other recurring shell failure patterns.
flowchart LR S["script"] --> P["ShellCheck parser"] P --> R["rules"] R --> D["diagnostics"] D --> F["fix or documented suppression"]
2. Run ShellCheck on the declared shell dialect
shellcheck scripts/*.shA correct shebang helps ShellCheck determine whether the file targets Bash, POSIX sh, or another shell.
3. Quoting findings often prevent real data corruption
files=("one file.txt" "two.txt")
# Fragile:
# for file in ${files[@]}; do rm -- $file; done
# Correct:
for file in "${files[@]}"; do
rm -- "$file"
doneStatic diagnostics catch word splitting and glob expansion before they become pathname bugs.
4. Learn from diagnostic codes
shellcheck --format=gcc script.shCodes such as SC2086 and SC2046 are stable references to detailed explanations and examples.
5. Set severity policy deliberately
shellcheck \
--severity=warning \
scripts/ci/*.shTeams can decide which severity levels block CI while still surfacing lower-level style findings.
6. Help ShellCheck resolve sourced libraries
# shellcheck source=lib/common.sh
source "$SCRIPT_DIR/lib/common.sh"A directive can document the intended source file when the runtime path is dynamic.
7. Suppress only intentional findings
# Intentional trusted compiler flag splitting.
# shellcheck disable=SC2086
compiler $EXTRA_FLAGS source.cA suppression should explain why the flagged behavior is safe and intentional.
8. Match ShellCheck to the portability claim
shellcheck --shell=sh portable.sh
shellcheck --shell=bash bash-only.shDo not advertise POSIX portability while relying on Bash arrays, [[ ]], or process substitution.
9. Make analysis reproducible in CI
find scripts -type f -name '*.sh' -print0 |
xargs -0 shellcheckNUL-delimited discovery keeps unusual filenames from breaking the lint step.
10. Analyzer upgrades are intentional changes
New ShellCheck versions may add rules. Pinning or deliberately upgrading the version makes newly enforced diagnostics reviewable.
11. Hands-on lab: lint and repair
mkdir -p "$HOME/devops-academy/bash/chapter17/lesson02"
cd "$HOME/devops-academy/bash/chapter17/lesson02"
cat > bad.sh <<'EOF'
#!/usr/bin/env bash
files=("one file.txt" "two.txt")
for f in ${files[@]}; do
echo Processing $f
done
EOF
cat > good.sh <<'EOF'
#!/usr/bin/env bash
set -u
files=("one file.txt" "two.txt")
for f in "${files[@]}"; do
printf 'Processing %s\n' "$f"
done
EOF
if command -v shellcheck >/dev/null 2>&1; then
shellcheck bad.sh || true
shellcheck good.sh
else
printf 'ShellCheck not installed; inspect the files manually.\n' >&2
fiVerification checklist
12. Knowledge check
Question 1. Does ShellCheck execute scripts?
Question 2. Why are diagnostic codes useful?
Question 3. When is suppression justified?
Question 4. Why keep ShellCheck in CI?
13. Summary
ShellCheck turns common shell hazards into fast feedback. Run it consistently, understand the rule codes, fix rather than suppress by default, and align it with your actual target shell.
14. Further reading
- ShellCheck documentation and rule wiki.
- GNU Bash Reference Manual — quoting and expansions.
- POSIX shell specification.
- ShellCheck command-line help.
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0
Send only Ethereum/ERC-20 compatible assets to this address.