Shellcheck 可发现你的 shell 脚本中的 bug
ShellCheck 是一个用于 shell 脚本的静态分析工具,因能迅速发现从遗留维护脚本到现代 CI 流水线中的微妙 bug 和危险模式而备受称赞。评论者描述了将它集成到 pre-commit hooks 和 GitLab CI 包装器中的做法,同时也提到 Dockerfile lint 工具和 bash 语言服务器等配套工具。与此同时,很多人认为复杂自动化应当完全从 shell 中移出,转而使用 Python、Haskell 或 Swift 等语言,只把 shell 作为围绕命令行工具的薄薄胶水,并依赖 lint 工具来保持即便这种狭窄用法也足够安全。
对 ShellCheck 的整体评价
- 被广泛赞为“必备”工具;即使是非常有经验的 shell 用户,也能发现微妙的 bug。
- 尤其适合大型或遗留 shell 脚本库,以及 CI 流水线。
- 一些人认为它重要到足以强制执行“如果 ShellCheck 失败,就不运行脚本”,通过包装器或严格的 shebang 来实现。
在 CI / 开发工作流中的集成
- 常见模式:通过 pre-commit hooks 和 CI 作业运行 ShellCheck。
- 痛点:嵌在其他文件里的 shell(例如 GitLab CI YAML、GitHub Actions、Dockerfiles、Justfiles)更难分析;一些用户编写了包装器或 hooks 来提取并检查这些片段。
- 有些人主张把所有非平凡的 shell 放到单独的脚本里,再由 CI 调用,这样既便于 lint,也更具可移植性。
- Dagger 等替代方案旨在用真正的编程语言来编码 CI/CD 流水线;讨论中也涉及其成熟度和 GitLab 集成细节。
Shell 与其他语言
- 分歧很大:
- 一派:尽可能避免使用 shell;尤其是在复杂逻辑或安全敏感代码中,改用 Python、Ruby、Go、Haskell、Swift 等。
- 另一派:shell 非常适合小型“胶水”脚本和 CI 任务;没有比它更容易把 CLI 工具组合起来的方式。
- Python 经常被提议作为主要替代方案,但人们抱怨其打包、分发和运行时开销。
- 有些人表示已成功将 Bash 迁移到 Haskell(Turtle/Shh)或 Swift(Shwift),获得了类型安全和结构化能力。
局限与边缘情况
- ShellCheck 在以下方面会有困难:
- 跨多个文件的
source/导入。 - Zsh(支持已移除;强制使用
--shell=bash只能部分奏效)。 - 一些安全问题,例如算术展开注入,除非配置得更严格。
- Bash 版本差异(
set -u、空数组、关联数组、[[ -v ... ]]行为在不同版本间可能不同)。
- 跨多个文件的
- 用户经常会自定义规则(例如禁用
${var}的样式检查或强制引用)。
讨论中的 Shell 最佳实践
- 常见建议:
set -u、-e、-o pipefail、用于 dry-run 的-n,以及广泛使用trap做清理。 - 对于把选项放在 shebang 里还是用
set的争论(可移植性和调用方式方面的顾虑)。 - 有些人提倡使用“严格模式”包装器,或使用 shellharden 之类的自动修复工具,来强制更安全、更一致的脚本风格。