getopts 的简单模拟(09.12 Rev)

       鉴于有些老版本的 busybox 可能没带 getopts 或 getopt 工具,为了写个支持选项的通用脚本,写个函数模拟 getopts,相比之前 shell 的选项解析 中的处理方式,这样也许更简单易用

关于 getopts 与 getopt

       处理命令行参数是个相似而又复杂的事,为此,C 提供了 getopt/getopt_long 等函数,C++ 的 boost 提供了 options 库,shell 中处理此事的是 getopts 和 getopt
      getopts 是 Shell 内置(builtin)命令,只支持短选项,而 getopt 属于外部命令,支持长短选项(绕开了 getopts 不符合 Linux 约定的限制),并具有一些 getopts 没有的特性

getopts 的惯用法

格式: getopts  option_string  opt_var  [args...],若无 args,则获取命令行参数
样例:
while getopts ":a:bc" opt  ##  第一个冒号表示忽略错误;字符后的冒号表示该选项带一个参数
do
        case $opt in  ## opt 保存了解析出的选项名
                a ) echo $OPTARG  ## OPTARG 存储选项所带的参数
                    echo $OPTIND;;  ## OPTIND 存储下一个待处理选项在最初列表中的位置
                b ) echo "b $OPTIND";;
                c ) echo "c $OPTIND";;
                ? ) echo "error"
                    exit 1;;
        esac
done
shift $(($OPTIND - 1))  ## 这样 $* 就只保留除去选项的参数

      getopts 使用了两个隐含变量:一个是 OPTARG,用来取当前选项的参数,另外一个是 OPTIND(选项索引),用来取要处理的下一个选项,初始值为 1。这与 libc 中的 getopt 函数实现相近,可参考这篇:getopt 函数分析
      若 options-strings 开始有冒号(忽略错误),则
a. 当指定的选项不存在时,opt_var 设置为 ":",对应的 $OPTARG 为"对应的选项"
b. 指定的选项带参数的而没有提供参数,opt_var 设为"?",对应的 $OPTARG 为"这时候的选项"
可以根据这两个选项指定不同的反馈信息

实现

      这里用全局变量来保存处理状态,_ARGS_ 保存待处理的命令行参数(内部使用),ARGS_保存其中非选项参数,OPT_VAR 保存识别出的选项,OPT_ARG 保存选项所带的参数,若选项不带参则为空。使用时当然自定义变量不能与之重名,否则程序行为将变为非线性。。。

      相比 getopts,有些特性没了,如 silent 模式,但增加了对长选项的简单支持,带参的长选项需写为 --long-opt=opt-arg 的形式;也支持连续选项,即类似 tar 的 -xvf 写法

 1 _ARGS_="$@"

 2 ARGS_=""

 3 OPT_VAR=""

 4 OPT_ARG=""

 5 

 6 ERR_ILLEGAL_OPT=65

 7 ERR_OPT_NEED_VALID_ARG=67

 8 ERR_UNKNOWN_PARA=69

 9 

10 get_opts() 11 {

12    _get_opts "$1" $_ARGS_  ## 转为位置参数 13    return $?

14 }

15 

16 _get_opts() 17 {

18    local opts="$1" && shift  ## 位置参数 1 为 opt-string

19    OPT_VAR="" && OPT_ARG=""  ## 双清

20  local arg

21    for arg in $_ARGS_  ## 寻找待处理选项 22    do

23       case $arg in

24       --* )  ## 1. 长选项 25             local asn_opt="${arg#--}"

26             OPT_VAR="${asn_opt%%=*}"

27             if [ "$OPT_VAR" != "$asn_opt" ]; then

28                 OPT_ARG="${asn_opt#*=}"

29                 [ -z "$OPT_ARG" ] && ErrorX $ERR_OPT_NEED_VALID_ARG  ## 若等号后未带参数,则出错退出 30             fi

31             ;;

32       -*  )  ## 2. 短选项 

33             local the_opt=${arg#-}

34             local o1=$(expr substr "$the_opt" 1 1)

35             [ $(expr index "$opts" "$o1") -eq 0 ] && ErrorX $ERR_ILLEGAL_OPT  ## 若解析出的选项在 opt-string 里没找到

36             OPT_VAR="$o1"

37             

38             if [ $(expr match "$opts" ".*${o1}:") -gt 0 ]; then

39                [ "$o1" != "$the_opt" -o -z "$2" ] && ErrorX $ERR_OPT_NEED_VALID_ARG

40                OPT_ARG=$2

41                shift

42             fi

43             ;;

44       *   )  ## 3. 保存非选项参数(显然不支持带空格的参数) 45             ARGS_="$ARGS_ $arg"

46             ;;

47       esac

48       shift

49       [ -n "$OPT_VAR" ] && break  ## 若找到选项则 break

50    done

51    _ARGS_="$@"  ## 保存剩余待处理参数

52    [ ${#the_opt} -gt 1 ] && _ARGS_="-${the_opt#?} $_ARGS_"  ## 支持连续选项

53    [ -n "$_ARGS_" -o -n "$OPT_VAR" ] && return 0 || return 1  ## 若有待处理的参数或选项,则返回 0

54 }

其中,ErrorX 是错误处理函数,统一处理错误码,“X”为退出意,当然是否退出可自定义

用法

与 getopts 类似,如:

 1 while get_opts "r:w:"

 2 do

 3     case $OPT_VAR in

 4     r  | retry    ) retry=$OPT_ARG;;  ## 重试次数  5     w | wait    ) interval=$OPT_ARG;;  ## 重试间隔  6        *        )  ErrorX $ERR_UNKNOWN_PARA;;

 8     esac

 9 done

10 [ -n "$ARGS_" ] && set $ARGS_  ## 将剩下的非选项参数设为位置参数,方便后继使用

命令行可写成这样:

./tool.sh -r 3 -w 30

或长选项形式:

./tool.sh --retry=3 --wait=30

 

看到这篇文章 命令行参数解析,发觉倒和 python 的选项有点像,不过看上去它一次就解析完了,呵呵

1 opts,args = getopt.getopt(sys.argv[1:],"h:p:",["host=","port="])

2 for opt,arg in opts:

3       if opt in ("-h","--host"):

4           host = arg

5       if opt in ("-p","--port"):

6           port = arg

 

你可能感兴趣的:(get)