ArgsHelper.sh

#!/bin/bash
#
# source BaseEnvHelper.sh or source ArgsHelper.sh to use
#   used to help other scripts to make their args organized
#   invoke 'SetRequestedArg' / 'SetDescription' / 'SetExample' first
#   then invoke 'VerifyAllArgs "$@"'

set -e
source ${HOME}/env-setup/env-helper/ColorStyleHelper.sh

# create empty arrays to store arguments with corresponding names
# shortNameArray to store shortName and so on
shortNameArray=()
fullNameArray=()
exampleNameArray=() # used to store [$shortName $fullName] together to output when argUsage() called
ifOptionalArray=()
argDescriptionArray=()
ifAcceptValueArray=()
validValueArray=()
ifMultipleArray=()

# it's VERY likely to create an array with non-continuous indices when VerfiyAllArgs() is called
# one or more indices may be 'jumped' over when one or more optional args are NOT passed-in
# so ONLY passed-in args and values are verified and then put into validPassedInValueArray
validPassedInArgArray=()
validPassedInValueArray=()

# call anywhere inside worker script after VerifyAllArgs() was called
# example and description are skipped
PukeMeOut() {
  local size=${#shortNameArray[*]}
  declare -i i=1 # skip [--help HELP_ARG]
  ifAll=$1
  while [ "$size" -gt "$i" ]; do
    if [ "$ifAll" == true ]; then # puke all build-in arrays and items
      if [ -z "${validPassedInValueArray[$i]}" ]; then
        validPassedInArgArray[$i]='None'
        validPassedInValueArray[$i]='None'
      fi
      # exampleName and exampleNameArray are skipped cause they already have styles
      echo -e "${_COLOR_D}\
shortName: [${shortNameArray[$i]}] \
| fullName: [${fullNameArray[$i]}] \
| ifOptional: [${ifOptionalArray[$i]}] \
| argDescription: [${argDescriptionArray[$i]}] \
| ifAcceptValue: [${ifAcceptValueArray[$i]}] \
| validValue: [${validValueArray[$i]}] \
| ifMultiple: [${ifMultipleArray[$i]}] \
| validPassedInArg: [${validPassedInArgArray[$i]}] \
| validPassedInValue: [${validPassedInValueArray[$i]}]\
${_END}"
    else # puke only arrays and itmes related to passed-in args
      if [ -z "${validPassedInValueArray[$i]}" ]; then
        i=$i+1
        continue
      fi
      echo -e "${_COLOR_D}\
validPassedInArg: [${validPassedInArgArray[$i]}] \
| fullName: [${fullNameArray[$i]}] \
| ifOptional: [${ifOptionalArray[$i]}] \
| ifAcceptValue: [${ifAcceptValueArray[$i]}] \
| validValue: [${validValueArray[$i]}] \
| ifMultiple: [${ifMultipleArray[$i]}] \
| validPassedInValue: [${validPassedInValueArray[$i]}]\
${_END}"
    fi
    i=$i+1
  done
}

description= # not an array
exampleArray=()
exampleDescArray=()

# invoke SetRequestedArg() multiple times to setup a list of required args
SetRequestedArg() {
  # 0 at the 1st time, increases by 1 on each invocation
  # so value can be set at correct index of target array
  argNum=${#shortNameArray[*]}

  # two examples:
  # 1. SetRequestedArg "-c" "true" "COMPRESS_FILE" "if compress the output file" "true" "zip tar.gz"
  # valid: '-c zip' or not passed-in, invalid: '-c bzip2'
  #
  # 2. SetRequestedArg "-os" "false" "OS_NAME" "name of operating system" "true" "win7 win10 win2008r2 win2012r2" "true"
  # valid: '-os "win7 win2k8r2"', invalid: '-os "win8 win10"' or not passed-in

  shortName=$1 # arg short name, used when call from command line, like '-c' or '-os'
  ifOptional=$2 # if optional or mendatory, 'true' or 'false'
  fullName=$3 # arg full name, used in script which sources ArgsHelper, like 'COMPRESS_FILE' or 'OS_NAME'
  argDescription=$4 # arg description, describe what is the arg used for
  ifAcceptValue=$5 # if accepta passing value from command line, 'true' or 'false' (false means the arg is like a switch, can be turned on or off)
  validValue=$6 # a list of valid values, used to validate passed-in values, like 'zip tar.gz' or 'win7 win10 win2008r2 win2012r2'
  ifMultiple=$7 # if arg can be passed-in multi-values, multi-values needs double quotes, see examples above

  # 'accepts value' means 'mendatory', so only 'optoinal' arg can accept no value
  if [ "$ifOptional" == "false" -a "$ifAcceptValue" == "false" ]; then
    echo -e "\n${_ERROR}ERROR: only optional arg can be set not to accept value${_END}"
    echo -e "${_ERROR}please re-define arg [$shortName $fullName] and try again${_END}"
    exit 100
  fi

  if [ "$shortName" == "--help" ]; then
    exampleName="${_B}${_COLOR_A}[$shortName]${_END}"
  else
    exampleName="${_B}${_COLOR_A}$shortName${_END} ${_UL}${_COLOR_A}$fullName${_END}"
    [ "$ifOptional" == "true" ] && exampleName="${_COLOR_A}[${_END}${exampleName} ${_COLOR_A}(optional)]${_END}"
    [ "$ifAcceptValue" == "false" ] && exampleName="${exampleName} ${_COLOR_A}(not accepts value)${_END}"
  fi

  shortNameArray[$argNum]=$shortName
  fullNameArray[$argNum]=$fullName
  ifOptionalArray[$argNum]=$ifOptional
  argDescriptionArray[$argNum]=$argDescription
  ifAcceptValueArray[$argNum]=$ifAcceptValue
  exampleNameArray[$argNum]=$exampleName
  validValueArray[$argNum]=$validValue
  ifMultipleArray[$argNum]=$ifMultiple
}

SetDescription() {
  description=$1
}

# example and its description
# if need to use customized format, like
# example:
#   info1
#     info2
#       info3
# just set 'exampleArray' but leave 'exampleDescArray' empty, like
# SetExample "example:\n  info1\n    info2\n      info3" ""
SetExample() {
  argNum=${#exampleArray[*]}
  exampleArray[$argNum]="$1"
  exampleDescArray[$argNum]="$2"
}

# will be invoked on each ERROR in 'shortMode', and '--help' with every details
argUsage() {
  argNum=${#shortNameArray[*]}
  exampleNum=${#exampleArray[*]}

  # create a brief argUsage with script name and arg list
  declare -i i=0
  while [ "$argNum" -gt "$i" ]; do
    argList="$argList ${exampleNameArray[$i]}"
    i=$i+1
  done

  if [ "$1" == "short" ]; then
    echo -e "\n${_COLOR_A}argUsage${_END}:$argList"
    echo -e "${_COLOR_A}run '$villain --help' for more details${_END}"
  else # display every details if not 'shortMode'
    if [ -n "$preLogs" ]; then
      echo -e "${_COLOR_D}----------------------------------------------------------------------------${_END}"
      echo -e "${_COLOR_D}  Functions called before ${FUNCNAME}:${_END}"
      for preLog in $preLogs; do
        echo -e "${_COLOR_D}    ${preLog}${_END}"
      done
      echo -e "${_COLOR_D}----------------------------------------------------------------------------${_END}"
    fi
    echo -e "\n${_4S}${_COLOR_A}argUsage:${_END}$argList"
    echo -e "${_4S}${_COLOR_D}$description${_END}\n"
    declare -i i=0
    while [ "$argNum" -gt "$i" ]; do
      echo -e "${_4S}${exampleNameArray[$i]}" # '[-c COMPRESS_FILE]'
      echo -e "${_4S}${_4S}${_COLOR_B}${argDescriptionArray[$i]}${_END}" # '(optional) if compress the output file'

      if [ "${ifAcceptValueArray[$i]}" == "true" ]; then
        if [ ! -z "${validValueArray[$i]}" ]; then
          if [ "${ifMultipleArray[$i]}" == "true" ]; then # 'Valid values: [win7 win10 win2008r2 win2012r2]'
            echo -e "${_4S}${_4S}${_COLOR_B}valid values: [${validValueArray[$i]}]\
            \n${_4S}${_4S}single('') or double(\"\") quotes required if multi-values passed-in${_END}"
          else
            echo -e "${_4S}${_4S}${_COLOR_B}valid value: [${validValueArray[$i]}]${_END}" # 'Valid value: [zip tar.gz]'
          fi
        else
          echo -e "${_4S}${_4S}${_COLOR_B}accepts any value (validation needs be done in worker script)${_END}" # 'Valid value: [zip tar.gz]'
        fi
      fi
      i=$i+1
    done

    if [ "$exampleNum" -gt 0 ]; then
      echo -e "\n${_4S}${_COLOR_C}Examples:${_END}"
      declare -i i=0
      while [ "$exampleNum" -gt "$i" ]; do
        echo -e "${_4S}${_4S}${_COLOR_D}${exampleArray[$i]}${_END}"
        echo -e "${_2S}${_4S}${_4S}${_COLOR_D}${exampleDescArray[$i]}${_END}\n"
        i=$i+1
      done
    fi
  fi
}

# exceptions=(noArgs), for now only one type of exception
# make an exception when some scripts need NO args to run
MakeException() {
  exception=$1
}

# used to check if required arg(s) are passed-in
# call "CheckArgBinds '-c XYZ' '-a -b'" inside the beginning of a function
# or code block before doing real work
CheckArgBinds() {
  local action=$1
  local binds=$2

  for bind in $binds; do # check each required arg is passed-in
    local shortName=${bind%:*}
    if [[ ! "${validPassedInArgArray[@]}" =~ "$shortName" ]]; then
      echo -e "${_ERROR}ERROR: Arguments Binds validation falied. [$action] requires [${bind/:/ }] to pass-in${_END}"
      exit 11
    fi
  done
}

# useful if want to output log for functions called before argUsage() or VerifyAllArgs()
# cause some values of args need to be generated before worker scripts actually run
preRunningLog=''
SetPreRunningLogs() {
  preRunningLog="${preRunningLog} ${1}"
}

# verify all args passed in, invoked by: 'VerifyAllArgs "$@"'
VerifyAllArgs() {
  if [ "$#" -eq 0 ] && [ "$exception" != 'noArgs' ]; then
    echo -e "\n${_ERROR}ERROR: no arguments passed-in${_END}"
    argUsage short
    exit 11
  fi

  if [ "$1" == "--help" ]; then
    argUsage # display every details if the 1st arg is '--help'
    exit 0
  fi

  echo -e "\n${_COLOR_D}----------------------------------------------------------------------------${_END}"
  if [ -n "$preRunningLog" ]; then
    echo -e "${_COLOR_D}  Functions called before ${FUNCNAME}:${_END}"
    for preLog in $preRunningLog; do
      echo -e "${_COLOR_D}    ${preLog}${_END}"
    done
    echo ""
  fi

  # print info of which worker script runs on which host with what arguments
  # will be useful if called by other script from remote
  echo -e "${_COLOR_D}  On Host [`hostname`] run full invocation as:${_END}"
  echo -e "${_COLOR_D}    $villain $@${_END}"
  echo -e "${_COLOR_D}----------------------------------------------------------------------------${_END}\n"

  ifPassedArray=() # create an array for checking every required arg is passed-in
  argNum=${#shortNameArray[*]}
  declare -i i=0
  while [ "$argNum" -gt "$i" ]; do
    ifPassedArray[$i]="false"
    i=$i+1
  done

  while [ "$#" -gt 0 ]; do
    found="false"
    declare -i i=0
    for short in ${shortNameArray[@]}; do # check if passed-in shortName is valid
      if [ "$short" == "$1" ]; then
        found="true"
        break
      fi
      i=$i+1
    done

    if [ "$found" == "true" ]; then # if shortName is valid
      validPassedInArgArray[$i]="$1"
      ifPassedArray[$i]="true"
      if [ "${ifAcceptValueArray[$i]}" == "true" ]; then # check if accepts values
        validValues="${validValueArray[$i]}"
        if [ -z "$2" ]; then
          echo -e "\n${_ERROR}ERROR: the arg [$1] accepts value but passed-in none${_END}"
          argUsage 'short'
          exit 11
        else # check if passed-in value is valid
          if [ ! -z "${validValues}" ]; then # if accepts value and has a list of valid value
            # iterate all passed-in values, exit on each invalid value
            if [ "${ifMultipleArray[$i]}" == "true" ]; then # if can pass-in multi-values
              for value_i in $2; do # iterate passed-in values
                valid="false"
                for value_j in $validValues; do # iterate the list of valid value
                  if [ "$value_i" == "$value_j" ]; then
                    valid="true"
                    break
                  fi
                done

                if [ "$valid" == "false" ]; then # passed-in value is invalid
                  echo -e "\n${_ERROR}ERROR: arg [$1] does not accept value [$value_i]${_END}"
                  echo -e "${_ERROR}please choose one or more among [${validValues}]${_END}"
                  echo -e "${_ERROR}multiple values need single or double quotes${_END}"
                  argUsage 'short'
                  exit 11
                fi
              done
            else
              # iterate all passed-in values, exit on each invalid value
              valid="false"
              for value in $validValues; do # iterate valid value list
                if [ "$value" == "$2" ]; then
                  valid="true"
                  break
                fi
              done

              if [ "$valid" == "false" ]; then # passed-in value is invalid
                echo -e "\n${_ERROR}ERROR: arg [$1] does not accept value [$2]${_END}"
                echo -e "${_ERROR}please choose one among [${validValues}]${_END}"
                argUsage 'short'
                exit 11
              fi
            fi
          fi

          # the condition of [ -z "${validValueArray[$i]}" ] needes no validation
          # so all passed-in value(s) should be valid till here, export them by 'eval'
          export_cmd="export ${fullNameArray[$i]}=\"$2\"" # make arg can accept values with space, like -x "AA BB"
          validPassedInValueArray[$i]="$2"
          eval "$export_cmd" # export arg and value to parent script
          shift; shift # shift arg and value
        fi
      else
        # simply set the arg to true if not accepts value, only applied to optional arg
        export_cmd="export ${fullNameArray[$i]}=true"
        validPassedInValueArray[$i]=true
        eval "$export_cmd"
        shift; # shift only arg
      fi
    else # if shortName is invalid
      echo -e "\n${_ERROR}ERROR: unknown arg [$1]${_END}"
      argUsage 'short'
      exit 11
    fi
  done

  declare -i i=0
  while [ "$argNum" -gt "$i" ]; do
    # if not optional but without passing value
    if [ "${ifOptionalArray[$i]}" == "false" -a "${ifPassedArray[$i]}" == "false" ]; then
      echo -e "\n${_ERROR}ERROR: missing required arg [${shortNameArray[$i]} ${fullNameArray[$i]}]${_END}"
      argUsage 'short'
      exit 11
    fi
    i=$i+1
  done
}

# add the 1st arg
SetRequestedArg "--help" "true" "HELP_ARG" "displays extended argUsage" "false"
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 224,815评论 6 522
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 96,251评论 3 402
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 171,999评论 0 366
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 60,996评论 1 300
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 69,993评论 6 400
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 53,477评论 1 314
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 41,848评论 3 428
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 40,823评论 0 279
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 47,361评论 1 324
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 39,401评论 3 346
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 41,518评论 1 354
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 37,119评论 5 351
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 42,850评论 3 338
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 33,292评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 34,424评论 1 275
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 50,072评论 3 381
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 46,588评论 2 365

推荐阅读更多精彩内容