getoptions 是一个优雅的 shell 脚本选项解析器,完全支持所有 POSIX shell。本文档系统性地介绍 getoptions 的核心概念、语法特性、高级技巧和实战应用,帮助开发者快速掌握命令行选项解析的精髓,构建专业、易用的 shell 脚本工具。

📋 目录


一、快速开始

1.1 安装 getoptions

方式一:使用 tar.gz 包安装
# 在鸿蒙PC上执行
tar -xzf ohos_getoptions_3.3.3.tar.gz
cp -r getoptions_3.3.3 /data/service/hnp/getoptions.org/
方式二:手动安装
# 复制文件到安装目录
mkdir -p /data/service/hnp/getoptions.org/getoptions_3.3.3/bin
mkdir -p /data/service/hnp/getoptions.org/getoptions_3.3.3/lib

cp bin/getoptions /data/service/hnp/getoptions.org/getoptions_3.3.3/bin/
cp bin/gengetoptions /data/service/hnp/getoptions.org/getoptions_3.3.3/bin/
cp -r lib/* /data/service/hnp/getoptions.org/getoptions_3.3.3/lib/
chmod +x /data/service/hnp/getoptions.org/getoptions_3.3.3/bin/*

# 添加到 PATH
export PATH=$PATH:/data/service/hnp/getoptions.org/getoptions_3.3.3/bin

1.2 验证安装

# 查看版本
getoptions --version
# 输出: v3.3.2

# 查看帮助
getoptions --help

1.3 第一个示例

创建脚本 hello.sh

#!/bin/sh

VERSION="1.0.0"

parser_definition() {
  setup   REST help:usage -- "Usage: hello.sh [options]... [arguments]..." ''
  msg -- 'Options:'
  flag    FLAG    -f --flag                 -- "takes no arguments"
  param   PARAM   -p --param                -- "takes one argument"
  option  OPTION  -o --option on:"default"  -- "takes one optional argument"
  disp    :usage  -h --help
  disp    VERSION    --version
}

eval "$(getoptions parser_definition) exit 1"

echo "FLAG: ${FLAG:-0}, PARAM: ${PARAM:-未设置}, OPTION: ${OPTION:-未设置}"
if [ $# -gt 0 ]; then
    echo "剩余参数: $@"
fi

运行脚本:

chmod +x hello.sh
export PATH=$PATH:/data/service/hnp/getoptions.org/getoptions_3.3.3/bin
./hello.sh -f --flag -p value --param value -o --option -ovalue --option=value 1 2 3

输出:

FLAG: 1, PARAM: value, OPTION: value
剩余参数: 1 2 3

二、基本语法

2.1 解析器定义

parser_definition 函数

所有选项解析都从定义 parser_definition 函数开始:

parser_definition() {
  # 选项定义
}
setup 函数

setup 函数用于配置解析器的基本行为:

setup REST help:usage -- "Usage: prog [options]..." ''

参数说明:

  • REST:存储剩余参数的变量名
  • help:usage:自动生成帮助信息,使用 usage 函数
  • --:分隔符
  • "Usage: ...":使用说明文本

2.2 选项类型

flag - 标志选项(无参数)
flag FLAG -f --flag -- "description"
  • FLAG:存储选项值的变量名
  • -f:短选项
  • --flag:长选项
  • "description":选项描述
param - 参数选项(必需参数)
param PARAM -p --param -- "description"
  • PARAM:存储参数值的变量名
  • -p:短选项
  • --param:长选项
  • "description":选项描述
option - 可选参数选项
option OPTION -o --option on:"default" -- "description"
  • OPTION:存储参数值的变量名
  • -o:短选项
  • --option:长选项
  • on:"default":默认值(当选项存在但未提供参数时)
  • "description":选项描述
disp - 显示选项(如帮助、版本)
disp :usage -h --help
disp VERSION --version
  • :usage:调用 usage 函数
  • VERSION:显示变量值

2.3 辅助函数

msg - 添加消息
msg -- 'Options:'
msg -- ''  # 空行
属性说明
属性 说明 示例
counter:true 计数器选项 flag VERBOSE -v --verbose counter:true init:=0
init:=0 初始值 flag FLAG -f --flag init:=0
on:"default" 默认值(option) option OPT -o --opt on:"default"
validate:func 验证函数 param NUM -n --number validate:number
pattern:"regex" 模式匹配 param VAL -v --value pattern:"^[0-9]+$"

三、使用示例

3.1 基本用法:作为命令使用

方式一:使用函数定义(推荐)

export PATH=$PATH:/data/service/hnp/getoptions.org/getoptions_3.3.3/bin

test_getoptions() {
    VERSION="1.0.0"
    PROG="test_script"
    if ! command -v getoptions >/dev/null 2>&1; then
        echo "错误: 未找到 getoptions 命令"
        return 1
    fi
    parser_definition() {
      setup   REST help:usage -- "Usage: $PROG [options]... [arguments]..." ""
      msg -- "Options:"
      flag    FLAG    -f --flag                 -- "takes no arguments"
      param   PARAM   -p --param                -- "takes one argument"
      option  OPTION  -o --option on:"default"  -- "takes one optional argument"
      disp    :usage  -h --help
      disp    VERSION    --version
    }
    eval "$(getoptions parser_definition) exit 1"
    echo "=== 选项解析结果 ==="
    echo "FLAG: ${FLAG:-0}"
    echo "PARAM: ${PARAM:-未设置}"
    echo "OPTION: ${OPTION:-未设置}"
    if [ $# -gt 0 ]; then
        echo "剩余参数:"
        i=1
        for arg in "$@"; do
            echo "  $i: $arg"
            i=$((i + 1))
        done
    fi
}

# 运行测试
test_getoptions -f --flag -p value --param value -o --option -ovalue --option=value 1 2 3

输出:

=== 选项解析结果 ===
FLAG: 1
PARAM: value
OPTION: value
剩余参数:
  1: 1
  2: 2
  3: 3

3.2 查看帮助信息

test_getoptions --help

输出:

Usage: test_script [options]... [arguments]...

Options:
  -f, --flag                  takes no arguments
  -p, --param PARAM           takes one argument
  -o, --option[=OPTION]       takes one optional argument
      --help
      --version

3.3 计数器选项

export PATH=$PATH:/data/service/hnp/getoptions.org/getoptions_3.3.3/bin

test_verbose() {
    PROG="verbose_test"
    if ! command -v getoptions >/dev/null 2>&1; then
        echo "错误: 未找到 getoptions 命令"
        return 1
    fi
    parser_definition() {
      setup REST help:usage -- "Usage: $PROG [options]..." ""
      flag VERBOSE -v --verbose counter:true init:=0 -- "verbose level"
      disp :usage -h --help
    }
    eval "$(getoptions parser_definition) exit 1"
    echo "Verbose level: $VERBOSE"
}

# 运行测试
test_verbose -vvv
# 输出: Verbose level: 3

3.4 作为库使用

test_library() {
    GETOPTIONS_LIB="/data/service/hnp/getoptions.org/getoptions_3.3.3/lib/getoptions_base.sh"
    if [ ! -f "$GETOPTIONS_LIB" ]; then
        echo "错误: 未找到库文件"
        return 1
    fi
    . "$GETOPTIONS_LIB"
    PROG="library_test"
    parser_definition() {
      setup REST help:usage -- "Usage: $PROG [options]..." ""
      flag FLAG -f --flag -- "a flag option"
      param VALUE -v --value -- "a value option"
      disp :usage -h --help
    }
    eval "$(getoptions parser_definition parse)"
    parse "$@"
    eval "set -- $REST"
    echo "FLAG: ${FLAG:-0}, VALUE: ${VALUE:-未设置}"
    if [ $# -gt 0 ]; then
        echo "剩余参数: $@"
    fi
}

# 运行测试
test_library -f --value test arg1 arg2

3.5 子命令支持

export PATH=$PATH:/data/service/hnp/getoptions.org/getoptions_3.3.3/bin

test_subcmd() {
    PROG="subcmd_test"
    if ! command -v getoptions >/dev/null 2>&1; then
        echo "错误: 未找到 getoptions 命令"
        return 1
    fi
    parser_definition() {
      setup REST help:usage -- "Usage: $PROG [options] <command>..." ""
      flag VERBOSE -v --verbose -- "verbose mode"
      disp :usage -h --help
    }
    cmd_parser_definition() {
      setup REST help:usage -- "Usage: $PROG cmd [options]..." ""
      flag FLAG -f --flag -- "command flag"
      disp :usage -h --help
    }
    eval "$(getoptions parser_definition parse) exit 1"
    parse "$@"
    eval "set -- $REST"
    case "${1:-}" in
      cmd)
        shift
        eval "$(getoptions cmd_parser_definition cmd_parse) exit 1"
        cmd_parse "$@"
        eval "set -- $REST"
        echo "Command executed with flag: ${FLAG:-0}, verbose: ${VERBOSE:-0}"
        ;;
      *)
        echo "Unknown command: ${1:-}"
        return 1
        ;;
    esac
}

# 运行测试
test_subcmd cmd -f arg1 arg2

3.6 参数验证

export PATH=$PATH:/data/service/hnp/getoptions.org/getoptions_3.3.3/bin

test_validate() {
    PROG="validate_test"
    if ! command -v getoptions >/dev/null 2>&1; then
        echo "错误: 未找到 getoptions 命令"
        return 1
    fi
    number() {
      case $OPTARG in
        (*[!0-9]*) return 1
      esac
    }
    parser_definition() {
      setup REST help:usage -- "Usage: $PROG [options]..." ""
      param NUMBER -n --number validate:number -- "must be a number"
      disp :usage -h --help
    }
    eval "$(getoptions parser_definition) exit 1"
    echo "Number: ${NUMBER:-未设置}"
}

# 测试有效数字
test_validate -n 123
# 输出: Number: 123

# 测试无效输入(会报错)
test_validate -n abc
# 输出: Validation error (number): -n


四、高级功能

4.1 使用 gengetoptions 生成解析器

生成库文件
export PATH=$PATH:/data/service/hnp/getoptions.org/getoptions_3.3.3/bin

# 生成库文件
gengetoptions library > getoptions_lib.sh
生成解析器代码
# 假设 parser_definition.sh 包含 parser_definition 函数
gengetoptions parser -f parser_definition.sh parser_definition parse prog > parser.sh
使用生成的解析器
. ./parser.sh
parse "$@"
eval "set -- $REST"

4.2 自定义错误处理

parser_definition() {
  setup REST help:usage error:"custom_error" -- "Usage: prog [options]..." ""
  # ... 选项定义 ...
}

custom_error() {
  echo "自定义错误: $1" >&2
  exit 1
}

4.3 选项缩写

parser_definition() {
  setup REST help:usage abbr:true -- "Usage: prog [options]..." ""
  # ... 选项定义 ...
}

启用后,--verbose 可以缩写为 --verb--ver(只要不产生歧义)。

4.4 模式匹配

parser_definition() {
  setup REST help:usage -- "Usage: prog [options]..." ""
  param VALUE -v --value pattern:"^[0-9]+$" -- "must be a number"
}

4.5 组合选项

# 支持 -abc 等同于 -a -b -c
parser_definition() {
  setup REST help:usage -- "Usage: prog [options]..." ""
  flag FLAG_A -a --flag-a -- "flag a"
  flag FLAG_B -b --flag-b -- "flag b"
  flag FLAG_C -c --flag-c -- "flag c"
}

五、实际应用场景

5.1 Shell 脚本命令行工具

为 shell 脚本添加专业的命令行选项支持:

#!/bin/sh
parser_definition() {
  setup REST help:usage -- "Usage: tool.sh [options]..." ""
  flag VERBOSE -v --verbose -- "verbose mode"
  param CONFIG -c --config -- "config file"
  disp :usage -h --help
}
eval "$(getoptions parser_definition) exit 1"

5.2 复杂选项解析

支持长选项、可选参数、子命令等高级功能:

parser_definition() {
  setup REST help:usage -- "Usage: app.sh [options] <command>..." ""
  flag VERBOSE -v --verbose counter:true init:=0 -- "verbose level"
  option OUTPUT -o --output on:"stdout" -- "output file"
  disp :usage -h --help
}

5.3 跨平台脚本开发

利用 POSIX 兼容性,编写可在多个平台运行的脚本。

5.4 与 getopt/getopts 的对比

特性 getopt getopts getoptions
实现方式 外部命令 shell 内置 shell 脚本
可移植性
长选项 GNU only
可选参数 GNU only
子命令
自动帮助
参数验证

六、常见问题

Q1: 为什么选择 getoptions 而不是 getopt 或 getopts?

A: getoptions 提供更好的功能支持(长选项、可选参数、子命令等),完全 POSIX 兼容,无需外部命令,性能更好。

Q2: 如何在脚本中同时使用 getoptions 和 getopts?

A: getoptions 会正确重置 OPTARGOPTIND,可以在同一个脚本中安全使用。

Q3: 生成的解析器代码可以修改吗?

A: 可以,getoptions 使用 CC0 许可证,可以自由修改和使用。

Q4: 性能如何?

A: 根据官方基准测试(Ubuntu dash, Core i7 3.4 GHz):

  • 作为命令使用:约 4.9ms(有 15ms 外部命令开销)
  • 作为库使用:约 4.1ms
  • 作为生成器使用:约 827μs(最快)

Q5: 支持哪些 shell?

A: 支持所有 POSIX shell:sh、bash、dash、ksh、zsh、yash 等。

Q6: 如何处理选项后的参数?

A: 使用 REST 变量存储剩余参数:

eval "$(getoptions parser_definition) exit 1"
# REST 变量包含剩余参数的引用
eval "set -- $REST"
# 现在 $@ 包含所有剩余参数

Q7: 如何实现选项的互斥?

A: 在解析后手动检查:

eval "$(getoptions parser_definition) exit 1"
if [ -n "${FLAG_A:-}" ] && [ -n "${FLAG_B:-}" ]; then
    echo "错误: -a 和 -b 不能同时使用"
    exit 1
fi

Q8: 如何实现必需选项?

A: 在解析后手动检查:

eval "$(getoptions parser_definition) exit 1"
if [ -z "${REQUIRED_PARAM:-}" ]; then
    echo "错误: --param 是必需的"
    exit 1
fi

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐