shellcheck-configuration ยท diff
git:20260307.47a5dbc to git:20260522.be57c0b
2 added, 418 removed. Audit A to A.
---
name: shellcheck-configuration
description: Master ShellCheck static analysis configuration and usage for shell script quality. Use when setting up linting infrastructure, fixing code issues, or ensuring script portability.
---
# ShellCheck Configuration and Static Analysis
Comprehensive guidance for configuring and using ShellCheck to improve shell script quality, catch common pitfalls, and enforce best practices through static code analysis.
## When to Use This Skill
- Setting up linting for shell scripts in CI/CD pipelines
- Analyzing existing shell scripts for issues
- Understanding ShellCheck error codes and warnings
- Configuring ShellCheck for specific project requirements
- Integrating ShellCheck into development workflows
- Suppressing false positives and configuring rule sets
- Enforcing consistent code quality standards
- Migrating scripts to meet quality gates
- ## ShellCheck Fundamentals
-
- ### What is ShellCheck?
-
- ShellCheck is a static analysis tool that analyzes shell scripts and detects problematic patterns. It supports:
-
- - Bash, sh, dash, ksh, and other POSIX shells
- - Over 100 different warnings and errors
- - Configuration for target shell and flags
- - Integration with editors and CI/CD systems
-
- ### Installation
-
- ```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
- ```
-
- ## Configuration Files
-
- ### .shellcheckrc (Project Level)
-
- 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=SC2086
- ```
-
- ### Environment Variables
-
- ```bash
- # Set default shell target
- export SHELLCHECK_SHELL=bash
-
- # Enable strict mode
- export SHELLCHECK_STRICT=true
-
- # Specify configuration file location
- export SHELLCHECK_CONFIG=~/.shellcheckrc
- ```
-
- ## Common ShellCheck Error Codes
-
- ### SC1000-1099: Parser Errors
-
- ```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
- ```
-
- ### SC2000-2099: Shell Issues
-
- ```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
- ```
-
- ### SC2100-2199: Quoting Issues
-
- ```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 )
- ```
-
- ### SC3000-3999: POSIX Compliance Issues
-
- ```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
- }
- ```
-
- ## Practical Configuration Examples
-
- ### Minimal Configuration (Strict POSIX)
-
- ```bash
- #!/bin/bash
- # Configure for maximum portability
-
- shellcheck \
- --shell=sh \
- --external-sources \
- --check-sourced \
- script.sh
- ```
-
- ### Development Configuration (Bash with Relaxed Rules)
-
- ```bash
- #!/bin/bash
- # Configure for Bash development
-
- shellcheck \
- --shell=bash \
- --exclude=SC1091,SC2119 \
- --enable=all \
- script.sh
- ```
-
- ### CI/CD Integration Configuration
-
- ```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
- ```
-
- ### .shellcheckrc for Project
-
- ```
- # 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=true
- ```
-
- ## Integration Patterns
-
- ### Pre-commit Hook Configuration
-
- ```bash
- #!/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
- ```
-
- ### GitHub Actions Workflow
-
- ```yaml
- name: 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 {} \;
- ```
-
- ### GitLab CI Pipeline
-
- ```yaml
- shellcheck:
- stage: lint
- image: koalaman/shellcheck-alpine
- script:
- - find . -type f -name "*.sh" -exec shellcheck {} \;
- allow_failure: false
- ```
-
- ## Handling ShellCheck Violations
-
- ### Suppressing Specific Warnings
-
- ```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
- ```
-
- ### Common Violations and Fixes
-
- #### SC2086: Double quote to prevent word splitting
-
- ```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
- ```
-
- #### SC2181: Check exit code directly
-
- ```bash
- # Problem
- some_command
- if [ $? -eq 0 ]; then
- echo "success"
- fi
-
- # Solution
- if some_command; then
- echo "success"
- fi
- ```
-
- #### SC2015: Use if-then instead of && ||
-
- ```bash
- # Problem
- [ -f "$file" ] && echo "exists" || echo "not found"
-
- # Solution - clearer intent
- if [ -f "$file" ]; then
- echo "exists"
- else
- echo "not found"
- fi
- ```
-
- #### SC2016: Expressions don't expand in single quotes
-
- ```bash
- # Problem
- echo 'Variable value: $VAR'
-
- # Solution
- echo "Variable value: $VAR"
- ```
-
- #### SC2009: Use pgrep instead of grep
-
- ```bash
- # Problem
- ps aux | grep -v grep | grep myprocess
-
- # Solution
- pgrep -f myprocess
- ```
-
- ## Performance Optimization
-
- ### Checking Multiple Files
-
- ```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
- ```
-
- ### Caching Results
-
- ```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
- ```
-
- ## Output Formats
-
- ### Default Format
-
- ```bash
- shellcheck script.sh
-
- # Output:
- # script.sh:1:3: warning: foo is referenced but not assigned. [SC2154]
- ```
-
- ### GCC Format (for CI/CD)
-
- ```bash
- shellcheck --format=gcc script.sh
-
- # Output:
- # script.sh:1:3: warning: foo is referenced but not assigned.
- ```
-
- ### JSON Format (for parsing)
-
- ```bash
- shellcheck --format=json script.sh
-
- # Output:
- # [{"file": "script.sh", "line": 1, "column": 3, "level": "warning", "code": 2154, "message": "..."}]
- ```
-
- ### Quiet Format
-
- ```bash
- shellcheck --format=quiet script.sh
+ ## Detailed patterns and worked examples
- # Returns non-zero if issues found, no output otherwise
- ```
+ Detailed pattern documentation lives in `references/details.md`. Read that file when the navigation tier above is insufficient.
## Best Practices
1. **Run ShellCheck in CI/CD** - Catch issues before merging
2. **Configure for your target shell** - Don't analyze bash as sh
3. **Document exclusions** - Explain why violations are suppressed
4. **Address violations** - Don't just disable warnings
5. **Enable strict mode** - Use `--enable=all` with careful exclusions
6. **Update regularly** - Keep ShellCheck current for new checks
7. **Use pre-commit hooks** - Catch issues locally before pushing
8. **Integrate with editors** - Get real-time feedback during development