ShellCheck
相关工具推荐
| 工具 | 说明 |
|---|---|
| Shfmt | Shell 脚本格式化工具 |
| Pre-commit | Git 提交门禁框架 |
1. 简介
ShellCheck 是 Shell/Bash 脚本的静态分析工具,由 Vidar Holen 创建并维护,采用 GPLv3 许可证发布,使用 Haskell 语言编写。它能够检测 Shell 脚本中的语法错误、语义问题、典型陷阱和代码风格问题,并为每条告警提供详细的修复建议和 Wiki 文档链接。
- 主要检查语言:Shell(Bash、dash、ksh)
- 主要检查能力:对 sh/bash/dash/ksh 脚本进行静态分析,检测语法错误、语义缺陷、兼容性问题和代码异味;涵盖质量类检查(语法错误、语义缺陷)+ 风格类检查(代码风格建议)+ 兼容性检查(POSIX 可移植性)
- 核心检查原理:基于 Shell AST 分析
- 检查规则/选项:约 280+ 条 SC 规则(SC1000–SC2236,每条规则有独立 wiki 页面),全量规则列表
- GitHub 仓库:https://github.com/koalaman/shellcheck(39,560 stars)
- 开源协议:GPL-3.0-or-later
- 最新稳定版本:v0.10.0
- 运行环境要求:无(独立二进制)
- 误报率:低
ShellCheck 是 Shell 脚本静态分析领域的主流工具之一,在 DevOps 和基础设施自动化项目中被广泛采用。
2. 官方文档
| 资源 | 链接 | 说明 |
|---|---|---|
| GitHub 仓库 | https://github.com/koalaman/shellcheck | 源码、Issue、Release |
| 在线检查 | https://www.shellcheck.net | 在线 Web 版本 |
| 规则 Wiki | https://www.shellcheck.net/wiki/ | 所有检查规则的完整索引 |
| 集成文档 | https://github.com/koalaman/shellcheck/wiki/Integration | 输出格式、退出码、环境变量等集成说明 |
| 手册页源文件 | https://github.com/koalaman/shellcheck/blob/master/shellcheck.1.md | 命令行参数、指令、RC 文件等完整参考 |
| Release 下载 | https://github.com/koalaman/shellcheck/releases | 各平台预编译二进制文件 |
3. 社区优秀实践
3.1 ShellCheck 官方 pre-commit 钩子仓库
ShellCheck 官方维护了独立的 pre-commit 钩子仓库 koalaman/shellcheck-precommit,使用 Docker 镜像运行 ShellCheck,确保版本一致性。该仓库的 .pre-commit-hooks.yaml 定义了官方 hook 配置。
- 仓库地址:https://github.com/koalaman/shellcheck-precommit
- 实践要点:
- 使用
language: docker_image,通过 Docker 容器运行 ShellCheck,无需本地安装 - hook 类型默认匹配
types: [shell],自动识别 Shell 脚本文件 - 版本与 ShellCheck release 同步更新(当前 v0.11.0)
- 使用
3.2 Homebrew
Homebrew 是 macOS 和 Linux 上主流的包管理器之一,其核心仓库 Homebrew/brew 包含大量 Shell 脚本。Homebrew 在 CI 流水线中使用 ShellCheck 对 Shell 脚本进行质量检查。
- 仓库地址:https://github.com/Homebrew/brew
- 生态参考项目
3.3 VS Code ShellCheck 扩展
vscode-shellcheck 是 ShellCheck 在 VS Code 中的官方推荐扩展,由社区维护,内置了多平台 ShellCheck 二进制文件,开箱即用。
- 仓库地址:https://github.com/timonwong/vscode-shellcheck
- 实践要点:
- 内置 Linux(x86_64/arm64/arm)、macOS(x86_64/arm64)、Windows(x86_64/arm64)的 ShellCheck 二进制
- 支持
onType(实时)和onSave(保存时)两种检查模式 - 支持
source.fixAll.shellcheck自动修复 - 通过
.shellcheckrc项目配置文件进行规则定制
4. 工具配置说明
4.1 配置文件说明
ShellCheck 支持多层级的配置方式,优先级从高到低为:源码内联指令 > 命令行参数 > RC 配置文件。
ShellCheck 涉及以下配置文件:
| 配置文件 | 用途 | 使用场景 |
|---|---|---|
.shellcheckrc |
ShellCheck RC 配置文件(INI 格式),声明 Shell 方言、告警级别、排除规则、启用可选检查等 | 所有项目,控制检查行为 |
配置文件查找顺序:
- 命令行
--norc选项可跳过所有 RC 文件 - 命令行
--rcfile指定的文件 - 脚本所在目录及逐级父目录中的
.shellcheckrc或shellcheckrc ~/.shellcheckrc$XDG_CONFIG_HOME/shellcheckrc(通常为~/.config/shellcheckrc)
4.2 .shellcheckrc 配置详解
.shellcheckrc 是 ShellCheck 的 RC 配置文件,ShellCheck 会自动从脚本所在目录逐级向上查找,然后查找 ~/.shellcheckrc 和 $XDG_CONFIG_HOME/shellcheckrc。
配置项说明:
| 配置项 | 类型 | 说明 |
|---|---|---|
shell |
string | 指定 Shell 方言(如 bash、sh、dash、ksh) |
severity |
string | 最低告警级别(error、warning、info、style),低于此级别的告警被忽略 |
exclude |
list[string] | 排除的规则编号列表,逗号分隔(如 SC1090,SC1091) |
external-sources |
bool | 是否跟踪 source 引用的外部文件(默认 false) |
source-path |
list[string] | 外部文件的搜索路径列表(如 SCRIPTDIR、lib) |
enable |
list[string] | 启用的可选检查列表,逗号分隔(如 add-default-case,check-unassigned-uppercase) |
推荐配置示例:
基础配置(适用于大多数 Shell 脚本项目):在保持合理检查覆盖的同时避免过多噪音。
# .shellcheckrc - ShellCheck 基础配置
# 指定 Shell 方言为 Bash
# 说明:确保检查规则基于 Bash 语法,避免 POSIX 兼容性误报
shell=bash
# 设置最低告警级别为 warning
# 说明:忽略 info 和 style 级别的建议,仅报告 warning 和 error
severity=warning
# 跟踪 source 引用的外部文件
# 说明:避免 SC1090(无法跟踪 source)和 SC1091(无法打开文件)误报
external-sources=true
# 排除动态 source 文件路径相关的规则
# SC1090: 无法跟踪动态 source 路径(如 source "$DIR/lib.sh")
# SC1091: 无法打开被 source 的文件进行检查
# 说明:动态路径在静态分析时无法解析,通常需要运行时上下文
exclude=SC1090,SC1091
严格配置(适用于生产环境脚本、基础设施自动化等对质量要求极高的场景):
# .shellcheckrc - ShellCheck 严格配置
# 指定 Shell 方言为 Bash
shell=bash
# 设置最低告警级别为 style(最低级别,报告所有问题)
severity=style
# 跟踪 source 引用的外部文件
external-sources=true
# 指定外部文件的搜索路径
# 说明:帮助 ShellCheck 找到被 source 的文件
source-path=SCRIPTDIR
source-path=lib
# 启用可选检查
# add-default-case: 建议 switch/case 语句添加 default 分支
# check-unassigned-uppercase: 检查未赋值的大写变量(可能为环境变量)
# quote-safe-variables: 对已知安全值的未引用变量发出警告
enable=add-default-case,check-unassigned-uppercase,quote-safe-variables
# 排除特定规则
# SC1090: 无法跟踪动态 source 路径
# SC1091: 无法打开被 source 的文件
exclude=SC1090,SC1091
4.3 常用规则编号速查
| 规则编号 | 严重级别 | 说明 | 修复建议 |
|---|---|---|---|
| SC2086 | warning | 变量未加双引号,可能导致单词拆分和通配符展开 | 使用 "$var" 替代 $var |
| SC2181 | error | 使用 $? 检查退出码,而非直接检查命令 |
使用 if ! cmd; then ... 替代 cmd; if [ $? -ne 0 ] |
| SC2006 | info | 使用反引号执行命令 | 使用 $(cmd) 替代 `cmd` |
| SC2003 | info | 使用过时的 expr 命令 |
使用 $((..)) 或 ${} 替代 |
| SC2164 | error | cd 命令未检查退出状态 |
使用 `cd dir |
| SC2034 | info | 变量已赋值但未使用 | 检查变量名拼写或删除未使用的变量 |
| SC2154 | warning | 变量被引用但未赋值 | 检查变量名拼写或添加赋值语句 |
| SC1090 | info | 无法跟踪动态 source 路径 |
添加 # shellcheck source=/path/to/file 注释 |
| SC1091 | info | 无法打开被 source 的文件 |
确保 --external-sources 和 --source-path 配置正确 |
| SC2317 | warning | 命令看似不可达(可能被间接调用) | 使用 # shellcheck disable=SC2317 屏蔽 |
| SC2115 | error | 使用 rm -rf 删除可能为空的变量路径 |
添加路径非空检查 |
| SC1045 | error | if 条件表达式语法错误 |
检查 [ 后是否有空格 |
| SC2066 | warning | for 循环中错误使用双引号导致只迭代一次 |
移除变量外的双引号或使用数组 |
| SC2016 | info | 单引号中的表达式不会被展开 | 使用双引号 "..." 替代单引号 '...' |
所有规则的完整列表和详细说明请参考:https://www.shellcheck.net/wiki/
5. 主流集成方式
5.1 pre-commit 集成
ShellCheck 官方提供了独立的 pre-commit 钩子仓库,使用 Docker 镜像运行,适合作为本地提交门禁。
依赖来源与适用前提:
- 使用
language: docker_image,需要系统安装 Docker - 无需本地安装 ShellCheck,由 Docker 镜像提供
- pre-commit 框架默认仅将暂存区中变更文件传给 hook,天然支持增量检查
配置文件(.pre-commit-config.yaml):
repos:
- repo: https://github.com/koalaman/shellcheck-precommit
rev: v0.10.0
hooks:
- id: shellcheck
# 可选:仅显示 error 和 warning 级别
# args: ["--severity=warning"]
使用本地 ShellCheck(无需 Docker):
如果不想依赖 Docker,可以使用 language: system 配置,前提是系统已安装 ShellCheck:
repos:
- repo: https://github.com/koalaman/shellcheck-precommit
rev: v0.10.0
hooks:
- id: shellcheck
language: system
注意:将
language改为system后,hook 将使用系统 PATH 中的 ShellCheck,需确保已安装且版本兼容。
增量检查:pre-commit 框架默认仅将暂存区中变更文件传给 hook,天然实现文件级增量检查,无需额外配置。CI 中可通过 pre-commit run --all-files 做全量检查,或使用 --from-ref/--to-ref 做 PR 级增量。
5.2 IDE 集成
VS Code
安装扩展 timonwong.shellcheck,内置多平台 ShellCheck 二进制,开箱即用。
配置示例(.vscode/settings.json):
{
"shellcheck.enable": true,
"shellcheck.executablePath": "",
"shellcheck.exclude": [],
"shellcheck.customArgs": [],
"shellcheck.run": "onType",
"shellcheck.ignorePatterns": {
"**/*.zsh": true,
"**/*.zshrc": true
}
}
| 配置项 | 说明 |
|---|---|
shellcheck.enable |
是否启用(默认 true) |
shellcheck.executablePath |
ShellCheck 路径(默认使用内置二进制) |
shellcheck.exclude |
排除的规则编号列表 |
shellcheck.customArgs |
自定义命令行参数 |
shellcheck.run |
触发时机:onType / onSave |
保存时自动修复:
{
"editor.codeActionsOnSave": {
"source.fixAll.shellcheck": "explicit"
}
}
Vim / Neovim
通过 ALE(异步 Lint 引擎)集成 ShellCheck:
" ~/.vimrc 或 ~/.config/nvim/init.vim
Plug 'dense-analysis/ale'
let g:ale_linters = {
\ 'sh': ['shellcheck'],
\ 'bash': ['shellcheck']
\}
let g:ale_sh_shellcheck_options = '--shell=bash --external-sources'
其他方案:Neomake、Syntastic 也支持 ShellCheck。
Emacs
Sublime Text
通过 SublimeLinter-shellcheck 集成。
5.3 命令行使用方式
常用命令
# 检查单个文件
shellcheck script.sh
# 检查多个文件
shellcheck script1.sh script2.sh
# 从标准输入读取
cat script.sh | shellcheck -
# 从文件列表读取
shellcheck --files-from=scriptlist.txt
常用参数
# 指定 Shell 方言(sh/bash/dash/ksh)
shellcheck -s bash script.sh
# 设置最低告警级别(error/warning/info/style)
shellcheck --severity=warning script.sh
# 排除特定规则
shellcheck --exclude=SC2086,SC1090 script.sh
# 仅包含特定规则
shellcheck --include=SC2086,SC2181 script.sh
# 跟踪 source 引用的外部文件
shellcheck --external-sources script.sh
# 指定外部文件的搜索路径
shellcheck --source-path=lib:scripts script.sh
# 启用可选检查
shellcheck --enable=add-default-case script.sh
# 启用所有可选检查
shellcheck --enable=all script.sh
# 不加载 RC 配置文件
shellcheck --norc script.sh
输出格式
# 默认 TTY 格式(人类可读)
shellcheck script.sh
# GCC 兼容格式(适用于编辑器集成)
shellcheck -f gcc script.sh
# CheckStyle XML 格式(适用于 CI 工具)
shellcheck -f checkstyle script.sh
# JSON 格式(适用于程序化处理)
shellcheck -f json1 script.sh
# diff 格式(可应用自动修复)
shellcheck -f diff script.sh
增量检查:ShellCheck 没有内置增量参数,可结合 git diff 筛选变更文件后传递给 shellcheck:
# 仅检查暂存区(staged)中新增或修改的 .sh 文件
git diff --cached --name-only --diff-filter=ACMR -- '*.sh' | xargs shellcheck
# 仅检查相对于 HEAD 变更的 .sh 文件
git diff --name-only --diff-filter=ACMR HEAD -- '*.sh' | xargs shellcheck
# 仅检查工作区中修改的 .sh 文件
git diff --name-only --diff-filter=ACMR -- '*.sh' | xargs shellcheck
--diff-filter=ACMR表示仅包含新增(A)、复制(C)、修改(M)、重命名(R)的文件,排除删除的文件。
此外,ShellCheck 提供了 --files-from 参数(v0.10.0 新增),可以从文件中读取待检查的文件列表,便于与外部脚本配合实现增量检查:
# 生成变更文件列表,然后通过 --files-from 传入
git diff --cached --name-only --diff-filter=ACMR -- '*.sh' > /tmp/files.txt
shellcheck --files-from=/tmp/files.txt
CI 脚本调用:在 CI 环境中通过脚本调用 ShellCheck 进行检查。
GitHub Actions
方式一:使用系统包管理器
name: ShellCheck
on: [push, pull_request]
jobs:
shellcheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run ShellCheck
run: |
sudo apt-get update && sudo apt-get install -y shellcheck
find . -name '*.sh' -not -path './vendor/*' \
-exec shellcheck --severity=warning {} +
方式二:使用预编译二进制(锁定版本)
name: ShellCheck
on: [push, pull_request]
jobs:
shellcheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Download ShellCheck
run: |
wget -qO- https://github.com/koalaman/shellcheck/releases/download/v0.10.0/shellcheck-v0.10.0.linux.x86_64.tar.xz \
| tar -xJv
sudo mv shellcheck-v0.10.0/shellcheck /usr/local/bin/
- name: Run ShellCheck
run: shellcheck --severity=warning scripts/*.sh
GitLab CI
shellcheck:
stage: lint
image: koalaman/shellcheck-alpine:stable
script:
- shellcheck --severity=warning scripts/*.sh
rules:
- changes:
- "**/*.sh"
注意:
koalaman/shellcheck-alpine是 ShellCheck 官方提供的 Alpine Linux 基础镜像,预装 ShellCheck。
增量检查:GitHub Actions 中可仅对变更文件运行 ShellCheck:
- name: Run ShellCheck on changed files
run: |
git diff --name-only --diff-filter=d ${{ github.event.before }}...${{ github.event.after }} \
| grep '\.sh$' \
| xargs shellcheck --severity=warning
GitLab CI 中按 MR 变更文件触发增量检查:
shellcheck:
stage: lint
image: koalaman/shellcheck-alpine:stable
script:
- git diff --name-only --diff-filter=d $CI_MERGE_REQUEST_DIFF_BASE_SHA...HEAD -- '*.sh' | xargs shellcheck --severity=warning
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
5.4 包管理器安装(生态主流)
ShellCheck 的主要安装方式是通过系统包管理器或预编译二进制,这是 Shell 生态中最主流的方式。
# macOS
brew install shellcheck
# Debian/Ubuntu
sudo apt install shellcheck
# Fedora
dnf install ShellCheck
# Arch Linux
pacman -S shellcheck
# Windows (winget)
winget install --id koalaman.shellcheck
# Windows (scoop)
scoop install shellcheck
# Docker
docker run --rm -v "$PWD:/mnt" koalaman/shellcheck:stable myscript
# Nix
nix-env -iA nixpkgs.shellcheck
预编译二进制可从 GitHub Releases 下载,支持 Linux(x86_64/armv6hf/aarch64/riscv64)、macOS(x86_64/aarch64)、Windows(x86)。
6. 告警抑制(屏蔽)方法
6.1 通过源码内联注释屏蔽
通过在 Shell 脚本中添加 # shellcheck 指令来屏蔽特定规则。指令放在第一个命令之前为文件级,放在命令之前为该命令级。
行级屏蔽:
# shellcheck disable=SC2086
echo $VAR
块级屏蔽(屏蔽多行):
# shellcheck disable=SC2086,SC2034
echo $VAR
UNUSED_VAR="hello"
# shellcheck enable=SC2086,SC2034
函数级屏蔽:
# shellcheck disable=SC2317
start() {
echo "Starting"
/etc/init.d/foo start
}
文件级屏蔽(放在 shebang 之后、任何命令之前):
#!/bin/bash
# shellcheck disable=SC2086,SC2034,SC2317
echo $VAR
6.2 通过 RC 配置文件屏蔽
在 .shellcheckrc 中通过 disable 字段全局排除特定规则:
# .shellcheckrc
disable=SC2086,SC1090,SC1091,SC2034
6.3 通过命令行参数屏蔽
# 排除特定规则
shellcheck --exclude=SC2086,SC1090 script.sh
# 设置最低告警级别(低于该级别的告警全部屏蔽)
shellcheck --severity=error script.sh
--severity的有效值(按严重程度从高到低):error>warning>info>style
6.4 通过 pre-commit 框架屏蔽
通过 pre-commit 的 files、exclude 参数控制 hook 作用的文件范围:
repos:
- repo: https://github.com/koalaman/shellcheck-precommit
rev: v0.10.0
hooks:
- id: shellcheck
# 仅检查 .sh 文件
files: \.sh$
# 排除 vendor 目录
exclude: ^vendor/
官方文档:pre-commit 配置
6.5 通过 VS Code 扩展屏蔽
在 settings.json 中配置排除规则:
{
"shellcheck.exclude": ["SC1090", "SC1091"]
}
或通过 shellcheck.ignorePatterns 忽略特定文件:
{
"shellcheck.ignorePatterns": {
"**/*.zsh": true,
"**/vendor/**/*.sh": true
}
}
6.6 各方式对比
| 屏蔽方式 | 适用场景 | 粒度 | 持久性 | 官方文档 |
|---|---|---|---|---|
| 源码内联注释 | 需要解释为何屏蔽时 | 行/块/函数/文件 | 随代码提交 | Directives |
| RC 配置文件 | 项目级全局排除 | 项目/目录 | 随项目配置 | RC Files |
| 命令行参数 | 临时调试或 CI 定制 | 单次执行 | 仅当次运行 | OPTIONS |
| pre-commit 配置 | 控制 hook 作用范围 | 文件匹配模式 | 随项目配置 | pre-commit 配置 |
| VS Code 设置 | 开发者个人偏好 | 工作区/用户 | 随 IDE 设置 | vscode-shellcheck |