Shell语言检查工具选型与落地建议
1. 工具选型表
| 工具 | 简介 | 优先级 | 误报率 | 告警抑制(屏蔽)方式 | 适用场景 |
|---|---|---|---|---|---|
| Shfmt | 格式化:AST 解析,多方言+EditorConfig | 必选 | 无 | 工具配置 | 本地增量、本地全量、PR增量、PR全量 |
| ShellCheck | 静态分析:600+ 规则,附修复建议 | 必选 | 低 | 行级注释、块级注释、文件级注释、工具配置 | 本地增量、本地全量、PR增量、PR全量 |
| pre-commit | 调度框架:声明式 YAML,依赖隔离 | 推荐 | 无 | 工具配置 | 本地增量、PR全量 |
| Gitleaks | 密钥检测:正则+熵分析,递归解码 | 推荐 | 低 | 行级注释、工具配置 | 本地增量、PR增量、PR全量、主干全量 |
| Codespell | 拼写检查:Wikipedia 常见错误字典 | 推荐 | 低 | 行级注释、工具配置 | 本地增量、PR增量、PR全量 |
2. 主流社区参考
2.1 Homebrew
Homebrew 是 macOS/Linux 包管理器,其 Shell 脚本检查实践值得借鉴。Homebrew 通过 .shellcheckrc 配置 ShellCheck 规则,在 CI 中强制执行,并针对 formula 文件的特定场景禁用部分规则(如 SC2034)。
- 仓库地址:https://github.com/Homebrew/brew (⭐ 48.9k)
- 使用工具:ShellCheck(静态分析)、Vale(文档 lint)、EditorConfig
- 工作流:通过 GitHub Actions 在 CI 中运行 ShellCheck;本地通过 .shellcheckrc 配置检查规则;未使用 pre-commit 框架,将检查直接放入 CI 工作流
- 关键配置:.shellcheckrc、.vale.ini、.editorconfig、.github/workflows/
- 借鉴价值:ShellCheck 是 Bash/zsh 生态的事实标准 linter;.shellcheckrc 可针对项目特定场景禁用部分规则;不使用 pre-commit 框架而直接放入 CI 也是可行的方案
2.2 oh-my-zsh
oh-my-zsh 是最流行的 zsh 框架,仓库以 zsh 脚本为主。oh-my-zsh 未使用专门的 Shell linter,但维护了一致的 .editorconfig,并用 Prettier 格式化工具链中的 JS 辅助脚本。
- 仓库地址:https://github.com/ohmyzsh/ohmyzsh (⭐ 188.7k)
- 使用工具:Prettier(格式化 JS/JSON)、EditorConfig
- 工作流:通过 GitHub Actions 运行 Prettier;仓库本身以 zsh 脚本为主,未使用专门的 Shell linter
- 关键配置:.editorconfig、.prettierrc、.github/workflows/
- 借鉴价值:纯 Shell 项目(如 zsh 插件/主题)通常不强制集成 ShellCheck,但维护一致的 .editorconfig 是基本要求;Prettier 用于工具链中的 JS 辅助脚本格式化
2.3 Bash-it
Bash-it 是推荐采用 pre-commit + ShellCheck + shfmt 组合的典型样本——pre-commit 提供统一入口,shfmt 处理缩进/对齐格式化,ShellCheck 做静态分析。这是社区中可复制的标准化方案。
- 仓库地址:https://github.com/Bash-it/bash-it (⭐ 15.1k)
- 使用工具:pre-commit 框架、ShellCheck、shfmt(通过 pre-commit hooks)、EditorConfig、ack
- 工作流:使用 pre-commit 框架统一管理本地与 CI 检查;通过 .pre-commit-config.yaml 声明 shfmt 和 shellcheck hooks;CI 复用同一配置
- 关键配置:.pre-commit-config.yaml、.editorconfig、.ackrc、.github/workflows/
- 借鉴价值:中等规模 Shell 项目推荐采用 pre-commit + ShellCheck + shfmt 的组合;pre-commit 提供统一入口,shfmt 处理格式化,ShellCheck 做静态分析,是社区中可复制的标准化方案
2.4 fish-shell
fish-shell 是友好的交互式 shell 实现,Shell 代码占比 21.9%、Rust 70.3%。CI 中有独立的 shellcheck job,通过 cargo xtask shellcheck 统一调用 ShellCheck 检查所有 Shell 脚本。
- 仓库地址:https://github.com/fish-shell/fish-shell (⭐ 33.9k)
- 使用工具:ShellCheck、rustfmt、Clippy、clang-format、ruff
- 工作流:Cargo + CMake 构建 + GitHub Actions,
lint.yml拆分为 format、shellcheck、clippy、rustdoc 多个独立 job - 关键配置:
.github/workflows/lint.yml、.clang-format、.rustfmt.toml、clippy.toml、.editorconfig - 借鉴价值:展示了 Shell 项目在 CI 中以独立 job 运行 ShellCheck 的最佳实践;通过
cargo xtask封装 ShellCheck 命令统一调用入口;多语言项目的 lint 工作流拆分策略
2.5 oh-my-bash
Oh My Bash 是 Bash 配置框架(Oh My Zsh 的 Bash 移植),纯 Shell 项目。通过 .shellcheckrc 配置 ShellCheck 规则,CI 中使用 editorconfig-checker 检查格式合规性。
- 仓库地址:https://github.com/ohmybash/oh-my-bash (⭐ 7.6k)
- 使用工具:ShellCheck、editorconfig-checker
- 工作流:GitHub Actions,
format.yml运行 editorconfig-checker 检查变更文件,test.yml在 Linux/Windows/macOS 三平台矩阵运行安装测试 - 关键配置:
.shellcheckrc、.github/workflows/format.yml、.editorconfig - 借鉴价值:演示了纯 Shell 项目通过
.shellcheckrc定制 ShellCheck 规则(如针对兼容性需求禁用 SC1087);EditorConfig + editorconfig-checker 的格式约束实践;跨平台 CI 矩阵验证 Shell 框架可移植性
3. 工程配置建议
Shell 生态主流项目(Homebrew、Bash-it、oh-my-bash)以 ShellCheck + shfmt 为核心工具链,Bash-it 展示了 pre-commit + ShellCheck + shfmt 的标准化组合。工程配置以 pre-commit 为代码仓级统一调度入口,官方基础 hook 覆盖全仓文件级问题,Shell 专用的 shfmt 和 ShellCheck 通过 pre-commit hook 调用。
3.1 工程配置文件汇总
| 配置文件 | 配置内容/作用 | 使用场景 | 维护建议 |
|---|---|---|---|
.shellcheckrc |
配置 ShellCheck 规则、shell 方言、排除规则 | ShellCheck 检查时读取 | 新项目直接创建;存量项目按需调整 |
.editorconfig |
统一脚本缩进和换行风格 | 编辑器保存时格式化 | 全仓通用,与 Shfmt 配合 |
.pre-commit-config.yaml |
官方基础 hook、密钥检测、拼写检查、Shfmt、ShellCheck | 本地提交前、PR 轻量门禁 | 由仓库统一维护版本;新增 hook 应说明耗时和误报处理方式 |
.gitignore / 工具 ignore 文件 |
排除构建产物、生成代码、第三方代码 | 本地、PR、全量检查 | 生成代码和 vendored 目录优先集中排除 |
.github/workflows/* |
PR 门禁、主干全量、夜间任务 | PR、主干、发布前、夜间 | CI 命令应尽量复用本地命令 |
3.2 .pre-commit-config.yaml
Shell 项目推荐以 pre-commit 作为代码仓级统一调度入口:先使用 pre-commit 官方基础 hook 覆盖全仓文件级问题,再叠加密钥检测、拼写检查,最后追加 Shell 专用的 Shfmt 与 ShellCheck。这样即使仓库后续增加 Markdown、JSON、YAML 等文件,也能保持统一的提交前检查入口。
# .pre-commit-config.yaml
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
hooks:
- id: trailing-whitespace # 移除行尾空白字符
- id: end-of-file-fixer # 确保文件以换行符结尾
- id: check-yaml # 校验 YAML 文件语法
- id: check-json # 校验 JSON 文件语法
- id: check-xml # 校验 XML 文件语法
- id: check-merge-conflict # 检查未解决的合并冲突标记
- id: check-added-large-files # 拦截超大文件提交(默认 500KB)
# args: ["--maxkb", "500"] # 支持自定义修改
- id: detect-private-key # 检测私钥/敏感信息泄露
# ===== 以下为可选工具,按需启用 =====
# - repo: https://github.com/gitleaks/gitleaks
# rev: v8.24.3
# hooks:
# - id: gitleaks # 密钥泄露检测
# - repo: https://github.com/codespell-project/codespell
# rev: v2.4.1
# hooks:
# - id: codespell # 拼写检查
# ===== Shell 必选工具:默认启用 =====
- repo: https://github.com/scop/pre-commit-shfmt
rev: v3.13.1-1
hooks:
- id: shfmt # Shell 脚本格式化(修复模式)
- repo: https://github.com/shellcheck-py/shellcheck-py
rev: v0.11.0.1
hooks:
- id: shellcheck # Shell 脚本静态分析
脚本集成测试、容器构建或端到端验证不适合放入 pre-commit,应在 PR 门禁、主干或夜间任务中单独执行。
4. 本地开发检查场景
本地开发以 pre-commit 为首选入口。Shfmt、ShellCheck、Gitleaks、Codespell 和官方基础 hook 均已由 .pre-commit-config.yaml 覆盖,本地默认检查只需要运行 pre-commit run --all-files,不需要再单独运行各工具原生命令。遇到误报时,先修正代码或配置,再使用 .shellcheckrc 或局部 # shellcheck disable=SCxxxx 做最小范围屏蔽。
4.1 本地检查流程
| 步骤 | 开发人员操作 | 依赖的工程配置 | 失败后怎么处理 |
|---|---|---|---|
| 安装工具 | python -m pip install pre-commit,然后 pre-commit install |
.pre-commit-config.yaml |
安装失败先确认 Python 版本和镜像源 |
| 自动修复 | 提交前自动触发 pre-commit hook | .pre-commit-config.yaml、.editorconfig |
根据 hook 名称定位失败工具,先修复问题 |
| 提交前检查 | pre-commit run --all-files 或提交时自动触发 |
.pre-commit-config.yaml、ignore 文件 |
格式问题由 Shfmt 自动修复,ShellCheck 问题按建议改代码 |
| 语言级检查 | 脚本集成测试等(如项目需要) | 测试配置、CI workflow | 本地无法复现时先同步依赖和 CI 环境版本 |
4.2 本地拦截与处理
| 拦截场景 | 常见原因 | 处理方式 | 是否可屏蔽 |
|---|---|---|---|
| 格式检查失败 | 未运行格式化、编辑器格式规则不一致 | Shfmt 自动修复并提交 | 通常不屏蔽,生成文件用 ignore 排除 |
| ShellCheck 失败 | 引用、数组、退出码、未定义变量等问题 | 按建议改代码;确实误报时用 # shellcheck disable=SCxxxx |
单行屏蔽必须写明规则和原因 |
| 密钥检测失败 | 提交了 token、私钥、连接串或测试凭据 | 删除密钥、轮换凭据 | 只有确认假阳性时可用 allowlist |
| 拼写检查失败 | 术语、品牌名、缩写未加入词典 | 修正拼写或加入 .codespellrc |
业务术语可集中加入词典 |
| 基础语法失败 | JSON/YAML/XML 不合法、脚本语法错误 | 修正语法或排除模板文件 | 模板文件可用 exclude 精确排除 |
5. PR 门禁检查场景
PR 门禁复用同一套 .pre-commit-config.yaml,确保本地和 CI 检查口径一致。PR 中建议对变更脚本增量检查;基础设施仓库建议主干再全量扫描所有脚本,避免长期遗漏 rarely touched 脚本。误报必须在代码评审里说明原因,优先通过集中配置或最小范围屏蔽处理。
5.1 PR 门禁使用的工程配置
| 配置 | PR 中的用途 | 开发人员如何复现 |
|---|---|---|
.pre-commit-config.yaml |
全仓轻量检查、密钥检测、拼写检查、Shell 格式和静态分析 | 本地运行 pre-commit run --all-files |
.shellcheckrc |
ShellCheck 规则和排除配置 | 本地确认排除规则是否覆盖对应路径 |
| CI workflow | 固定运行环境、工具安装、检查顺序 | 对照 workflow 的 run 命令逐条执行 |
5.2 PR 拦截与修复
| 拦截场景 | PR 中如何表现 | 开发人员处理方式 | 评审关注点 |
|---|---|---|---|
pre-commit 失败 |
CI 显示具体 hook 失败 | 本地运行同一 hook,提交修复结果 | 不接受直接跳过 hook 的提交 |
| 格式或 ShellCheck 失败 | CI 输出文件路径和规则编号 | Shfmt 自动修复;ShellCheck 按建议改代码 | 屏蔽必须限于最小范围 |
| 安全或密钥失败 | Gitleaks 报告高风险问题 | 删除敏感内容、轮换凭据、解释假阳性 | 高风险问题必须修复或经安全确认 |
| 历史问题暴露 | 全量任务发现大量旧问题 | 新增问题阻断,历史问题进入治理任务 | 不能让新代码扩大历史问题范围 |
5.3 Shell PR 门禁命令
name: Shell Quality
on: [pull_request]
jobs:
shell:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- run: python -m pip install pre-commit
- run: pre-commit run --all-files
全量检查(主干合并后、发布前、夜间任务)建议运行
pre-commit run --all-files覆盖全仓脚本;脚本集成测试和容器构建验证应在发布前或夜间任务中单独执行。误报处理遵循"先修正、再收敛规则、最后最小范围屏蔽"的顺序,具体屏蔽语法见 Shfmt 告警抑制 和 ShellCheck 告警抑制。
6. 落地步骤
- 先提交
.pre-commit-config.yaml、.shellcheckrc、.editorconfig和 CI 工作流,不立即阻断历史问题。 - 对新项目直接开启严格规则;对存量项目先只检查变更文件或建立 baseline。
- 在 README 或贡献指南中写清本地命令、PR 门禁命令和误报屏蔽要求。
- 将 Shfmt 设置为自动修复(
--write),ShellCheck 和安全扫描默认不自动改代码。 - 每次规则升级单独发 PR,避免与业务改动混在一起。
- 每季度清理一次 baseline、忽略列表和长期存在的屏蔽注释。