How to Keep a Shell Script ShellCheck-Clean from the Start
Choose the target shell, quote data, model arrays and pipelines correctly, and use focused ShellCheck directives only when the analyzer lacks necessary context.
ShellCheck is most useful as design feedback while a script is being written, not as a final ritual that produces a pile of suppressions.
Declare the language truthfully
Start with a shebang matching the features you intend to use:
#!/bin/sh
set -eu
or, when arrays and Bash-specific syntax are required:
#!/usr/bin/env bash
set -euo pipefail
Do not label a script sh and silence warnings about Bash syntax. Portability is a contract. Also understand that set -e has context-dependent behavior and is not exception handling; explicit status checks remain clearer for expected failures.
Quote expansions by default
input=${1:?usage: script INPUT}
cp -- "$input" "$destination/"
Quoted expansions preserve one argument even when values contain spaces or glob characters. Use printf, not echo, for data with unknown leading characters or escape sequences. In Bash, store lists as arrays rather than space-separated strings.
Make data flow visible
Check cd, temporary-file creation, and destructive preconditions explicitly. Avoid parsing ls. Prefer null-delimited interfaces when filenames can be arbitrary:
find "$root" -type f -print0 |
while IFS= read -r -d '' file; do
printf '%s\n' "$file"
done
That example requires a shell whose read supports -d; ShellCheck can help catch the mismatch if the shebang is honest.
Use directives as documented exceptions
Sometimes a sourced file is discovered dynamically or an expansion intentionally performs splitting. Put the narrowest # shellcheck disable=SC... directive beside the line and explain the invariant in a comment. A file-wide disable hides unrelated future bugs.
Run shellcheck in the editor and CI, pin or record the tool version when reproducibility matters, and test the script under every declared shell. Static analysis finds many expansion and portability mistakes, but it cannot prove the external commands, credentials, filesystem, and failure recovery all behave correctly.
Sources: