← 中文资源

简体中文版本

在 POSIX shell 里用 getopts 正确解析命令行参数

处理带参数短旗标、区分缺失值与空值,产出可行动的报错而不是静默默认。

阅读英文原文

目标

最吓人的 shell bug 是参数 bug:缺失值悄悄吞掉下一个旗标、空字符串通过真实性检查、叫 -weird 的目标被当成旗标吃掉。optstring 前导冒号加显式校验加 – 转义,二十行内把这些洞全部堵上,再用一张语法表把每种误用钉在 CI 里。

步骤要点

  1. 写 getopts 循环:optstring 用 “:e:r:vn” 前导冒号接管错误处理,: 分支报缺值、? 分支报未知旗标,usage 函数统一输出帮助。
  2. 循环结束后 shift $((OPTIND-1)) 拿位置参数,并对必填项做非空校验、对枚举值做 case 白名单校验。
  3. 支持 – 转义:位置参数以横线开头时(deploy.sh -e prod – -weird-name)原样接收而非被解析。
  4. 显式区分空值与缺值:-e “” 在解析期就报“value is empty”,而不是让空环境名流到下游。
  5. 用表驱动测试跑六行用例(缺值、未知旗标、空值、空串、合法、带 –),每行断言退出码并输出 ok,进 CI 防回归。

排错提示

需要 –env 长选项就在 getopts 前加一趟预处理把长翻译成短,或干脆文档化只用短旗标,POSIX 没有 lenovo 长选项内建;-e 后缺值吞掉 -v 是经典 bug,前导冒号让缺值走 : 分支而不是把下一个 token 当值吃掉;getopts 遇到第一个非选项就停,要么文档化“旗标在前”,要么 shift 位置参数后再跑第二趟 getopts 支持交错风格。

适用边界

getopts 只处理单字符旗标,更丰富的语法该交给有真正参数解析器的语言,而不是 shell 状态机模拟;语法表验证的是文法不是语义,旗标行为要有独立的测试。

代码示例

# the optstring colon is the whole trick:
#   ":e:r:vn"   -> silent errors you handle yourself
#   "e:r:vn"    -> getopts prints its own message and you lose control

while getopts ":e:r:vn" opt; do
  case "$opt" in
    e) ENV=$OPTARG ;;
    :) echo "error: -$OPTARG requires a value" >&2; usage ;;
    \?) echo "error: unknown flag -$OPTARG" >&2; usage ;;
  esac
done
shift $((OPTIND - 1))

# grammar table proving it
# ok [-e]        -> 2   (requires a value)
# ok [-z]        -> 2   (unknown flag)
# ok [-e ""]     -> 2   (empty value)
# ok [-e prod]   -> 0