zoukankan      html  css  js  c++  java
  • 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
  • 相关阅读:
    leetcode 29-> Divide Two Integers without using multiplication, division and mod operator
    ros topic 发布一次可能会接收不到数据
    python中的print()、str()和repr()的区别
    python 部分函数
    uiautomatorviewer错误 unable toconnect to adb
    pyqt 不规则形状窗口显示
    appium 计算器demo
    Spring 3.0 注解注入详解
    Spring Autowire自动装配
    restful 学习地址
  • 原文地址:https://www.cnblogs.com/lookbackinside/p/2676198.html
Copyright © 2011-2022 走看看