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 shellcheckbrew install shellcheck,或 shellcheck.net。
  • 常用:shellcheck script.sh;指定 shell:-s bash;跟踪source-x
  • 高频 SC:2086未引号变量、2164cd失败、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
SC2164cd dir失败未处理S05-02、S11-02
SC2181if cmd别只看$?S04-01
SC2006$( )别用反引号S09-03
SC2154变量未赋值就引用S03-01、set -uS01-04
SC2155local var=$(cmd)掩码退出码S08-02
SC2034变量「未使用」可能误报(export、read)
SC1090无法跟踪source非常量路径S08-03
SC3043pipefailsh不可用S13 可移植
SC2015set -eA && B || C注意S04-02

6. 示例:改前 / 改后

6.1 SC2086
# 警告rm-f$tmp# 修复rm-f--"$tmp"
6.2 SC2164
# 警告cd"$dir"do_work# 修复cd"$dir"||exit1do_work
6.3 SC2181
# 警告mycmdif[[$?-eq0]];then...# 修复ifmycmd;then...
6.4 SC2046
# 警告forfin$(ls*.log);do# 更好(专栏 S07-01)forfin*.log;do

7.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 -ecmd && 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-sbash

11. ShellCheck 不能发现什么

  • 业务逻辑错(删错文件但语法对)
  • 运行时网络/权限
  • 复杂依赖顺序
  • 所有可移植性(需 S13 + 多环境测)

静态 + 运行结合。


读脚本检查清单

  • 仓库是否约定ShellCheck/ CI?
  • 报告SC2086/2046是否已修引号?
  • SC2164cd是否|| exit
  • disable是否有注释理由?
  • shebang 与-s bash是否一致?

练习

判断题

  1. bash -n能发现$var未加引号的风险。
  2. SC2086 通常建议给变量加双引号。
  3. shellcheck -s bash按 Bash 规则分析。
  4. SC1090 表示 source 的文件一定不存在。
  5. 专栏只在 S12-02 集中讲 SC 编号,其它篇用通用表述。
参考答案
  1. 错。
  2. 对。
  3. 对。
  4. 错(常是静态分析跟不进动态路径)。
  5. 对(本篇为工具专篇)。

实操题

对下面片段,说出 ShellCheck最可能报哪类 SC(不必跑工具):

tmp=$(mktemp)cd$tmpdirrm-f$tmp
参考答案
  • SC2086$tmpdir$tmp未引号。
  • SC2164cd|| 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字面用-Fpipefail按需。


下一篇预告

S12-03:《排错方法论:最小复现与二分定位》— 完成 S12 模块,练「读一段卡住的脚本」。