Shellcheck finds bugs in your shell scripts

ShellCheck, a static analysis tool for shell scripts, is praised for quickly uncovering subtle bugs and dangerous patterns in everything from legacy maintenance scripts to modern CI pipelines. Commenters describe integrating it into pre-commit hooks and wrappers for GitLab CI, while also pointing to complementary tools like Dockerfile linters and bash language servers. At the same time, many argue that complex automation should move out of shell entirely into languages like Python, Haskell, or Swift, using shell only as thin glue around command-line tools and relying on linters to keep even that narrow usage safe.

Overall Sentiment on ShellCheck

  • Widely praised as a “must-have” tool; catches subtle bugs even for very experienced shell users.
  • Especially valued on large or legacy shell script bases and in CI pipelines.
  • Some see it as essential enough to enforce “no script runs if ShellCheck fails,” via wrappers or strict shebangs.

Integration in CI / Dev Workflow

  • Common pattern: run ShellCheck via pre-commit hooks and CI jobs.
  • Pain point: shell embedded in other files (e.g., GitLab CI YAML, GitHub Actions, Dockerfiles, Justfiles) is harder to analyze; several users built wrappers or hooks to extract and lint these sections.
  • Some advocate putting all non-trivial shell in separate scripts called from CI, both for linting and portability.
  • Alternatives like Dagger aim to encode CI/CD pipelines in real programming languages; debate over maturity and GitLab integration details.

Shell vs Other Languages

  • Strong disagreement:
    • One camp: avoid shell where possible; use Python, Ruby, Go, Haskell, Swift, etc., especially for complex logic or security-sensitive code.
    • Other camp: shell is ideal for small “glue” scripts and CI tasks; there is no easier way to compose CLI tools.
  • Python is often proposed as the main alternative, but people complain about packaging, distribution, and runtime overhead.
  • Some report successful migrations from Bash to Haskell (Turtle/Shh) or Swift (Shwift), gaining type safety and structure.

Limitations & Edge Cases

  • ShellCheck struggles with:
    • source/imports across multiple files.
    • Zsh (support removed; forcing --shell=bash only partially works).
    • Some security issues, e.g., arithmetic expansion injection, unless configured more strictly.
    • Bash version quirks (set -u, empty arrays, associative arrays, [[ -v ... ]] behavior differ across versions).
  • Users frequently customize rules (e.g., disabling style checks for ${var} or mandatory quoting).

Shell Best Practices Discussed

  • Frequent recommendations: set -u, -e, -o pipefail, -n for dry-run, and liberal use of trap for cleanup.
  • Debate over placing options in the shebang vs set (portability and invocation-style concerns).
  • Some promote a “strict mode” wrapper or auto-fix tools like shellharden to enforce safer, more consistent scripts.