美文网首页
ArgsHelper.sh

ArgsHelper.sh

作者: delusionxb | 来源:发表于2018-07-02 10:24 被阅读0次
    #!/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"
    

    相关文章

      网友评论

          本文标题:ArgsHelper.sh

          本文链接:https://www.haomeiwen.com/subject/nvpkuftx.html