示例说明
手写 while case 解析参数会踩遍陷阱:–output=value 形态、选项后参数、– 分隔符、缩写选项。getopt 把这些一次处理:定义串的冒号声明参数归属,重排后的参数流保证标志先于位置参数整齐排列,eval set 把重排结果装回位置参数。校验逻辑随后就简单——数字格式、必填位置参数逐个断言。
技术要点
- 定义串的冒号声明参数归属
- 重排保证标志先于位置参数
- – 分隔符正确终止选项解析
代码解读
解析循环按模式分支消费:无参标志 shift 一步、带参标志 shift 两步、– 是选项流终点;超时值用正则断言数字防注入奇怪值。循环结束后 $@ 只剩位置参数,数量校验与逐个存在性检查兜底。readonly 在解析完成后固化配置。
使用建议
macOS 自带的是 BSD getopt,需要 brew 的 GNU 版本或换 getopts 内建(无长选项)。选项默认值集中声明在解析前。usage 文本与定义串同步维护。
代码示例
#!/usr/bin/env bash
set -euo pipefail
# 选项定义:短串加长定义,冒号表示需要参数
readonly SHORT="vo:t:"
readonly LONG="verbose,output:,timeout:"
PARSED=$(getopt --alternative --options "${SHORT}" \
--longoptions "${LONG}" --name "$0" -- "$")
eval set -- "${PARSED}"
VERBOSE=false
OUTPUT="./out"
TIMEOUT=30
while true; do
case "$1" in
-v | --verbose) VERBOSE=true; shift ;;
-o | --output) OUTPUT="$2"; shift 2 ;;
-t | --timeout)
[[ "$2" =~ ^[0-9]+$ ]] || { echo "超时须为数字: $2" >&2; exit 2; }
TIMEOUT="$2"; shift 2 ;;
--) shift; break ;;
*) echo "未知参数: $1" >&2; exit 2 ;;
esac
done
[[ $# -ge 1 ]] || { echo "用法: $0 [-v] [-o 输出] [-t 超时] <输入...>" >&2; exit 2; }
readonly TIMEOUT
readonly OUTPUT
echo "verbose=${VERBOSE} output=${OUTPUT} timeout=${TIMEOUT}"
for input in "$@"; do
[[ -f "$input" ]] || { echo "输入不存在: ${input}" >&2; exit 1; }
echo "处理 ${input} -> ${OUTPUT}"
done