Shell脚本精读 · S12-02 | 语法检查与 ShellCheck:静态发现问题
模块:S12 调试、测试与排错
篇号:S12-02 / 42
预计阅读:45 分钟
主线:Bash
文章目录
- 本篇目标
- 30 秒速览
- 正文
- 1. 静态检查 vs 动态调试
- 2. `bash -n`:仅语法
- 3. 安装与基本用法
- 4. 输出长什么样
- 5. 高频 SC 与专栏篇目对照
- 6. 示例:改前 / 改后
- 6.1 SC2086
- 6.2 SC2164
- 6.3 SC2181
- 6.4 SC2046
- 7. `source` 与 SC1090
- 8. 抑制某条规则(慎用)
- 9. 与 `set` 选项
- 10. 工作流建议
- 11. ShellCheck 不能发现什么
- 读脚本检查清单
- 练习
- 判断题
- 实操题
- 改错题
- 下一篇预告
本篇目标
会用ShellCheck在不运行脚本的情况下发现引号、set、管道、可移植性等问题。能读懂SC 编号提示、用shellcheck命令与编辑器集成,并知道何时该改、何时可 disable。专栏其它篇不写 SC 小节;本篇集中讲工具(与 S12-01 动态-x互补)。
30 秒速览
bash -n:只查语法,不查逻辑与引号坑。- ShellCheck:静态分析,报SCxxxx编号与说明。
- 安装:
apt install shellcheck、brew install shellcheck,或 shellcheck.net。 - 常用:
shellcheck script.sh;指定 shell:-s bash;跟踪source:-x。 - 高频 SC:2086未引号变量、2164
cd失败、2181误用$?、2046命令替换未引号。 - disable要有理由;不要为消警告而乱关检查。
正文
1. 静态检查 vs 动态调试
| 手段 | 何时 | 本篇 / 篇目 |
|---|---|---|
| ShellCheck | 保存/提交前 | S12-02 |
set -x | 运行结果不对 | S12-01 |
| 最小复现 | 仍不清楚 | S12-03 |
shellcheckdeploy.sh不执行脚本,只分析文本。
2.bash -n:仅语法
bash-nscript.sh- 发现
if未闭合、引号不配等解析错误 - 不发现未引号变量、
set -u风险、管道逻辑问题
习惯:bash -n+ ShellCheck一起用。
3. 安装与基本用法
# Debian/Ubuntusudoaptinstallshellcheck# macOSbrewinstallshellcheck# 检查单文件shellcheckdeploy.sh# 明确按 Bash 规则(推荐本专栏脚本)shellcheck-sbashdeploy.sh# 跟踪 source 路径(分析被 source 的库)shellcheck-x-sbashmain.sh在线:把脚本贴到shellcheck.net(勿贴含密钥的产线脚本)。
4. 输出长什么样
deploy.sh:12:8: warning: Double quote to prevent globbing and word splitting. [SC2086] rm -f $tmp ^--^| 部分 | 含义 |
|---|---|
| 行:列 | 位置 |
| warning/error/info/style | 严重级别 |
| SC2086 | 规则编号(可查 wiki) |
| 下文 | 说明与建议 |
严重级别:
| 级别 | 建议 |
|---|---|
| error | 尽量修 |
| warning | 多数应修 |
| info/style | 视团队规范 |
5. 高频 SC 与专栏篇目对照
读 ShellCheck 报告时,可联想到专栏哪篇讲过:
| SC | 典型提示 | 专栏 |
|---|---|---|
| SC2086 | 变量应双引号 | S02-01、S02-03 |
| SC2046 | $(cmd)结果应引号 | S09-03、S07-01 |
| SC2068 | "$@"应引号 | S03-03 |
| SC2164 | cd dir失败未处理 | S05-02、S11-02 |
| SC2181 | 用if cmd别只看$? | S04-01 |
| SC2006 | 用$( )别用反引号 | S09-03 |
| SC2154 | 变量未赋值就引用 | S03-01、set -uS01-04 |
| SC2155 | local var=$(cmd)掩码退出码 | S08-02 |
| SC2034 | 变量「未使用」 | 可能误报(export、read) |
| SC1090 | 无法跟踪source非常量路径 | S08-03 |
| SC3043 | pipefail在sh不可用 | S13 可移植 |
| SC2015 | set -e下A && B || C注意 | S04-02 |
6. 示例:改前 / 改后
6.1 SC2086
# 警告rm-f$tmp# 修复rm-f--"$tmp"6.2 SC2164
# 警告cd"$dir"do_work# 修复cd"$dir"||exit1do_work6.3 SC2181
# 警告mycmdif[[$?-eq0]];then...# 修复ifmycmd;then...6.4 SC2046
# 警告forfin$(ls*.log);do# 更好(专栏 S07-01)forfin*.log;do7.source与 SC1090
source"${SCRIPT_DIR}/lib/common.sh"ShellCheck 可能报SC1090(无法解析非常量 source)。处理:
# 分析主脚本时跟踪 sourceshellcheck-x-sbashmain.sh# 或单独检查库shellcheck-sbashlib/common.sh读脚本:SC1090 不是运行时错误,是静态分析范围限制。
8. 抑制某条规则(慎用)
# 单行# shellcheck disable=SC2034UNUSED_FOR_EXPORT=1# 下一行# shellcheck disable=SC2086eval"legacy_code"# 仅当确知安全且有注释说明原则:
- 先修再 disable
- disable 旁写原因
- 不要
disable=SC2086整文件
9. 与set选项
ShellCheck 理解常见写法,会提示:
set -e与cmd && cmd || other组合(SC2015)set -u与未定义变量set -o pipefail在#!/bin/sh脚本(SC3043)
#!/bin/shset-opipefail# SC3043:POSIX sh 未必支持→ 改shebang 为 bash或去掉 pipefail(S13)。
10. 工作流建议
| 阶段 | 动作 |
|---|---|
| 编辑时 | VS Code/CursorShellCheck 扩展波浪线 |
| 提交前 | shellcheck -s bash *.sh |
| CI | 流水线跑 shellcheck,error 失败 |
| 排错时 | ShellCheck 通过后仍用S12-01-x |
findscripts-name'*.sh'-print0|xargs-0shellcheck-sbash11. ShellCheck 不能发现什么
- 业务逻辑错(删错文件但语法对)
- 运行时网络/权限
- 复杂依赖顺序
- 所有可移植性(需 S13 + 多环境测)
静态 + 运行结合。
读脚本检查清单
- 仓库是否约定ShellCheck/ CI?
- 报告SC2086/2046是否已修引号?
- SC2164
cd是否|| exit? - disable是否有注释理由?
- shebang 与
-s bash是否一致?
练习
判断题
bash -n能发现$var未加引号的风险。- SC2086 通常建议给变量加双引号。
shellcheck -s bash按 Bash 规则分析。- SC1090 表示 source 的文件一定不存在。
- 专栏只在 S12-02 集中讲 SC 编号,其它篇用通用表述。
- 错。
- 对。
- 对。
- 错(常是静态分析跟不进动态路径)。
- 对(本篇为工具专篇)。
实操题
对下面片段,说出 ShellCheck最可能报哪类 SC(不必跑工具):
tmp=$(mktemp)cd$tmpdirrm-f$tmp参考答案- SC2086:
$tmpdir、$tmp未引号。 - SC2164:
cd无|| exit。 - 若
tmpdir未定义还有SC2154(set -u 运行时也会炸)。
改错题
#!/usr/bin/env bashset-efiles=$(ls*.log)forfin$files;dogreppattern$f|wc-ldone参考#!/usr/bin/env bashset-euopipefailshopt-snullglobforfin*.log;dogrep-Fpattern --"$f"|wc-ldone消除SC2046/2086/2006类问题;grep字面用-F;pipefail按需。
下一篇预告
S12-03:《排错方法论:最小复现与二分定位》— 完成 S12 模块,练「读一段卡住的脚本」。