Chapter 17Lesson 02~100 minutes

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.

IntermediateTesting & debuggingHands-on lab

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.

Static-analysis workflow
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/*.sh

A 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"
done

Static diagnostics catch word splitting and glob expansion before they become pathname bugs.

4. Learn from diagnostic codes

shellcheck --format=gcc script.sh

Codes such as SC2086 and SC2046 are stable references to detailed explanations and examples.

5. Set severity policy deliberately

shellcheck \
  --severity=warning \
  scripts/ci/*.sh

Teams 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.c
Prefer fixes

A 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.sh

Do 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 shellcheck

NUL-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
fi

Verification 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.
Next lesson

Formatting Bash with shfmt

Continue Chapter 17 by turning Bash quality and debugging practices into a repeatable engineering workflow.

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.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.