Loading skill
Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Master ShellCheck static analysis configuration and usage for shell script quality. Use when setting up linting infrastructure, fixing code issues, or ensuring script portability.
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-05 | ✗→✓ | ▲ Improved | 102% | 0% |
| case-06 | ✗→✓ | ▲ Improved | 311% | 0% |
| case-15 | ✗→✓ | ▲ Improved | 145% | 0% |
| case-16 | ✗→✓ | ▲ Improved | 12% | 0% |
| case-23 | ✗→✓ | ▲ Improved | 145% | 0% |
Comprehensive guidance for configuring and using ShellCheck to improve shell script quality, catch common pitfalls, and enforce best practices through static code analysis.
resources/implementation-playbook.md.ShellCheck is a static analysis tool that analyzes shell scripts and detects problematic patterns. It supports:
bash# macOS with Homebrew brew install shellcheck # Ubuntu/Debian apt-get install shellcheck # From source git clone https://github.com/koalaman/shellcheck.git cd shellcheck make build make install # Verify installation shellcheck --version
Create .shellcheckrc in your project root:
# Specify target shell
shell=bash
# Enable optional checks
enable=avoid-nullary-conditions
enable=require-variable-braces
# Disable specific warnings
disable=SC1091
disable=SC2086bash# Set default shell target export SHELLCHECK_SHELL=bash # Enable strict mode export SHELLCHECK_STRICT=true # Specify configuration file location export SHELLCHECK_CONFIG=~/.shellcheckrc
bash# SC1004: Backslash continuation not followed by newline echo hello\ world # Error - needs line continuation # SC1008: Invalid data for operator `==' if [[ $var = "value" ]]; then # Space before == true fi
bash# SC2009: Consider using pgrep or pidof instead of grep|grep ps aux | grep -v grep | grep myprocess # Use pgrep instead # SC2012: Use `ls` only for viewing. Use `find` for reliable output for file in $(ls -la) # Better: use find or globbing # SC2015: Avoid using && and || instead of if-then-else [[ -f "$file" ]] && echo "found" || echo "not found" # Less clear # SC2016: Expressions don't expand in single quotes echo '$VAR' # Literal $VAR, not variable expansion # SC2026: This word is non-standard. Set POSIXLY_CORRECT # when using with scripts for other shells
bash# SC2086: Double quote to prevent globbing and word splitting for i in $list; do # Should be: for i in $list or for i in "$list" echo "$i" done # SC2115: Literal tilde in path not expanded. Use $HOME instead ~/.bashrc # In strings, use "$HOME/.bashrc" # SC2181: Check exit code directly with `if`, not indirectly in a list some_command if [ $? -eq 0 ]; then # Better: if some_command; then # SC2206: Quote to prevent word splitting or set IFS array=( $items ) # Should use: array=( $items )
bash# SC3010: In POSIX sh, use 'case' instead of 'cond && foo' [[ $var == "value" ]] && do_something # Not POSIX # SC3043: In POSIX sh, use 'local' is undefined function my_func() { local var=value # Not POSIX in some shells }
bash#!/bin/bash # Configure for maximum portability shellcheck \ --shell=sh \ --external-sources \ --check-sourced \ script.sh
bash#!/bin/bash # Configure for Bash development shellcheck \ --shell=bash \ --exclude=SC1091,SC2119 \ --enable=all \ script.sh
bash#!/bin/bash set -Eeuo pipefail # Analyze all shell scripts and fail on issues find . -type f -name "*.sh" | while read -r script; do echo "Checking: $script" shellcheck \ --shell=bash \ --format=gcc \ --exclude=SC1091 \ "$script" || exit 1 done
# Shell dialect to analyze against
shell=bash
# Enable optional checks
enable=avoid-nullary-conditions,require-variable-braces,check-unassigned-uppercase
# Disable specific warnings
# SC1091: Not following sourced files (many false positives)
disable=SC1091
# SC2119: Use function_name instead of function_name -- (arguments)
disable=SC2119
# External files to source for context
external-sources=truebash#!/bin/bash # .git/hooks/pre-commit #!/bin/bash set -e # Find all shell scripts changed in this commit git diff --cached --name-only | grep '\.sh$' | while read -r script; do echo "Linting: $script" if ! shellcheck "$script"; then echo "ShellCheck failed on $script" exit 1 fi done
yamlname: ShellCheck on: [push, pull_request] jobs: shellcheck: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Run ShellCheck run: | sudo apt-get install shellcheck find . -type f -name "*.sh" -exec shellcheck {} \;
yamlshellcheck: stage: lint image: koalaman/shellcheck-alpine script: - find . -type f -name "*.sh" -exec shellcheck {} \; allow_failure: false
bash#!/bin/bash # Disable warning for entire line # shellcheck disable=SC2086 for file in $(ls -la); do echo "$file" done # Disable for entire script # shellcheck disable=SC1091,SC2119 # Disable multiple warnings (format varies) command_that_fails() { # shellcheck disable=SC2015 [ -f "$1" ] && echo "found" || echo "not found" } # Disable specific check for source directive # shellcheck source=./helper.sh source helper.sh
bash# Problem for i in $list; do done # Solution for i in $list; do done # If $list is already quoted, or for i in "${list[@]}"; do done # If list is an array
bash# Problem some_command if [ $? -eq 0 ]; then echo "success" fi # Solution if some_command; then echo "success" fi
bash# Problem [ -f "$file" ] && echo "exists" || echo "not found" # Solution - clearer intent if [ -f "$file" ]; then echo "exists" else echo "not found" fi
bash# Problem echo 'Variable value: $VAR' # Solution echo "Variable value: $VAR"
bash# Problem ps aux | grep -v grep | grep myprocess # Solution pgrep -f myprocess
bash#!/bin/bash # Sequential checking for script in *.sh; do shellcheck "$script" done # Parallel checking (faster) find . -name "*.sh" -print0 | \ xargs -0 -P 4 -n 1 shellcheck
bash#!/bin/bash CACHE_DIR=".shellcheck_cache" mkdir -p "$CACHE_DIR" check_script() { local script="$1" local hash local cache_file hash=$(sha256sum "$script" | cut -d' ' -f1) cache_file="$CACHE_DIR/$hash" if [[ ! -f "$cache_file" ]]; then if shellcheck "$script" > "$cache_file" 2>&1; then touch "$cache_file.ok" else return 1 fi fi [[ -f "$cache_file.ok" ]] } find . -name "*.sh" | while read -r script; do check_script "$script" || exit 1 done
bashshellcheck script.sh # Output: # script.sh:1:3: warning: foo is referenced but not assigned. [SC2154]
bashshellcheck --format=gcc script.sh # Output: # script.sh:1:3: warning: foo is referenced but not assigned.
bashshellcheck --format=json script.sh # Output: # [{"file": "script.sh", "line": 1, "column": 3, "level": "warning", "code": 2154, "message": "..."}]
bashshellcheck --format=quiet script.sh # Returns non-zero if issues found, no output otherwise
--enable=all with careful exclusionsOther measured skills in the registry, with their headline benchmark lift.