Files
gh-hermeticormus-alqvimia-c…/skills/shellcheck-configuration/SKILL.md
2025-11-29 18:35:18 +08:00

455 lines
9.3 KiB
Markdown

---
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
# Returns non-zero if issues found, no output otherwise
```
## 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
## Resources
- **ShellCheck GitHub**: https://github.com/koalaman/shellcheck
- **ShellCheck Wiki**: https://www.shellcheck.net/wiki/
- **Error Code Reference**: https://www.shellcheck.net/