Bash getopts:选项解析、剩余位置参数和两种错误要分开
先写清命令行允许什么顺序
准备一个处理文件的小工具:-v 开启详细输出,-n 指定处理次数,其余参数是文件名。实现前先确定调用约定:选项放前面,文件放后面;文件名可能包含空格,也可能以横线开头。这个约定直接决定哪些输入是错误,哪些应被原样保留。下面只实现解析和回显,方便检查,不会修改任何输入文件。
Bash 官方文档说明,getopts 每次读取一个选项,把下一参数位置写入 OPTIND,需要的值写入 OPTARG。选项字符后的冒号表示必须带值;选项字符串最前面的冒号选择静默错误模式,此时缺值返回冒号标记,未知选项返回问号标记。静默指不自动打印诊断,并不表示错误已被处理。
AI生成概念示意图,非真实界面
让解析函数自己管理状态
下面整段保存为 Bash 脚本执行即可,不要用其他 sh 解释器替代。parse 函数用局部 OPTIND=1 开始每次解析,避免第二次调用沿用第一次的位置。测试函数把返回码打印出来,因此错误案例也能继续跑完。这里特意开启未定义变量检查,帮助发现误读 OPTARG 一类问题,但不依赖自动退出完成参数验证。
set -u
parse() {
local OPTIND=1 OPTARG opt
local verbose=0 count=1
while getopts ':vn:' opt; do
case "$opt" in
v) verbose=1 ;;
n) count=$OPTARG ;;
:) printf 'missing value: -%s\n' "$OPTARG" >&2; return 2 ;;
\?) printf 'unknown option: -%s\n' "$OPTARG" >&2; return 2 ;;
esac
done
shift "$((OPTIND - 1))"
if [[ ! $count =~ ^[1-9][0-9]*$ ]]; then
printf 'count must be a positive integer\n' >&2
return 2
fi
if (( $# == 0 )); then
printf 'at least one file is required\n' >&2
return 2
fi
printf 'verbose=%s count=%s files=%s\n' "$verbose" "$count" "$#"
printf 'file=[%s]\n' "$@"
}
run_case() {
local expected=$1 actual
shift
if parse "$@"; then actual=0; else actual=$?; fi
printf 'status=%s\n' "$actual"
[[ $actual == "$expected" ]] || return 1
}
run_case 0 -v -n 3 -- 'report one.txt' '-draft.txt' || exit 1
run_case 2 -n || exit 1
run_case 2 -x || exit 1
run_case 2 -n zero report.txt || exit 1
run_case 0 report.txt -v || exit 1
run_case 0 -n 2 next.txt || exit 1第一例应显示 verbose 为一、count 为三、files 为二;两行 file 输出分别完整保留 report one.txt 与 -draft.txt。双横线结束选项区,所以以横线开头的文件名没有进入未知选项分支。双引号包住的 "$@" 保留每一个原始参数边界,否则一个带空格的文件可能被拆成几份,后面再统计数量也已经晚了。
缺少值和格式不对是两个阶段
单独的 -n 没有后续值,走 missing value 分支;-x 走 unknown option 分支,两者都返回二。第三个错误案例提供了 zero,它在结构上确实是 -n 的参数,因此解析阶段接受它,随后由正整数规则拒绝。若把业务校验全寄托给 getopts,用户传入任意文本也可能进入后续处理。
还要注意,参数看起来像选项,不代表一定会被当成另一个选项。比如 -n 后面跟 -v,解析器可能把 -v 作为 -n 的值,再由这里的数字规则拒绝。错误提示因此应该说明次数值不合法,而不能武断声称用户一定忘写次数。示例没有做算术运算;若后续要重复执行,还应按业务限制最大次数。
剩余参数必须由脚本继续解释
shift 使用 OPTIND 减一,将已消费的部分移走,之后的位置参数才是文件列表。倒数第二例把 report.txt 放在 -v 前面,预期详细模式仍为零,文件数为二,后面的 -v 被作为文件参数保留。这是这里选择的接口规则。如果产品要求选项可穿插在文件之间,应重新设计解析流程,不能只在文档里许诺。
最后一例再次调用 parse,应得到 count 为二和一个文件,验证状态确实被重置。对能在同一进程里多次调用的函数,这个测试比单独重启脚本更容易暴露问题。上线前还应试空参数、空字符串文件名和重复 -n;本例重复次数选项会采用最后一次赋值,是否允许这种覆盖,需要在自己的工具说明中明确。
人工核对六次状态码依次为零、二、二、二、零、零,错误说明写到标准错误。
静默模式适合统一错误文案;默认模式会由 getopts 打印诊断,别再叠加一份相同消息。
缺值分支和未知选项分支都显式结束当前解析,避免带着不完整配置继续处理文件。


