An example of how to use getopts in bash

前端 未结 7 2254
小蘑菇
小蘑菇 2020-11-22 07:35

I want to call myscript file in this way:

$ ./myscript -s 45 -p any_string

or

$ ./myscript -h  #should display         


        
相关标签:
7条回答
  • 2020-11-22 08:09

    I know that this is already answered, but for the record and for anyone with the same requeriments as me I decided to post this related answer. The code is flooded with comments to explain the code.

    Updated answer:

    Save the file as getopt.sh:

    #!/bin/bash
    
    function get_variable_name_for_option {
        local OPT_DESC=${1}
        local OPTION=${2}
        local VAR=$(echo ${OPT_DESC} | sed -e "s/.*\[\?-${OPTION} \([A-Z_]\+\).*/\1/g" -e "s/.*\[\?-\(${OPTION}\).*/\1FLAG/g")
    
        if [[ "${VAR}" == "${1}" ]]; then
            echo ""
        else
            echo ${VAR}
        fi
    }
    
    function parse_options {
        local OPT_DESC=${1}
        local INPUT=$(get_input_for_getopts "${OPT_DESC}")
    
        shift
        while getopts ${INPUT} OPTION ${@};
        do
            [ ${OPTION} == "?" ] && usage
            VARNAME=$(get_variable_name_for_option "${OPT_DESC}" "${OPTION}")
                [ "${VARNAME}" != "" ] && eval "${VARNAME}=${OPTARG:-true}" # && printf "\t%s\n" "* Declaring ${VARNAME}=${!VARNAME} -- OPTIONS='$OPTION'"
        done
    
        check_for_required "${OPT_DESC}"
    
    }
    
    function check_for_required {
        local OPT_DESC=${1}
        local REQUIRED=$(get_required "${OPT_DESC}" | sed -e "s/\://g")
        while test -n "${REQUIRED}"; do
            OPTION=${REQUIRED:0:1}
            VARNAME=$(get_variable_name_for_option "${OPT_DESC}" "${OPTION}")
                    [ -z "${!VARNAME}" ] && printf "ERROR: %s\n" "Option -${OPTION} must been set." && usage
            REQUIRED=${REQUIRED:1}
        done
    }
    
    function get_input_for_getopts {
        local OPT_DESC=${1}
        echo ${OPT_DESC} | sed -e "s/\([a-zA-Z]\) [A-Z_]\+/\1:/g" -e "s/[][ -]//g"
    }
    
    function get_optional {
        local OPT_DESC=${1}
        echo ${OPT_DESC} | sed -e "s/[^[]*\(\[[^]]*\]\)[^[]*/\1/g" -e "s/\([a-zA-Z]\) [A-Z_]\+/\1:/g" -e "s/[][ -]//g"
    }
    
    function get_required {
        local OPT_DESC=${1}
        echo ${OPT_DESC} | sed -e "s/\([a-zA-Z]\) [A-Z_]\+/\1:/g" -e "s/\[[^[]*\]//g" -e "s/[][ -]//g"
    }
    
    function usage {
        printf "Usage:\n\t%s\n" "${0} ${OPT_DESC}"
        exit 10
    }
    

    Then you can use it like this:

    #!/bin/bash
    #
    # [ and ] defines optional arguments
    #
    
    # location to getopts.sh file
    source ./getopt.sh
    USAGE="-u USER -d DATABASE -p PASS -s SID [ -a START_DATE_TIME ]"
    parse_options "${USAGE}" ${@}
    
    echo ${USER}
    echo ${START_DATE_TIME}
    

    Old answer:

    I recently needed to use a generic approach. I came across with this solution:

    #!/bin/bash
    # Option Description:
    # -------------------
    #
    # Option description is based on getopts bash builtin. The description adds a variable name feature to be used
    # on future checks for required or optional values.
    # The option description adds "=>VARIABLE_NAME" string. Variable name should be UPPERCASE. Valid characters
    # are [A-Z_]*.
    #
    # A option description example:
    #   OPT_DESC="a:=>A_VARIABLE|b:=>B_VARIABLE|c=>C_VARIABLE"
    #
    # -a option will require a value (the colon means that) and should be saved in variable A_VARIABLE.
    # "|" is used to separate options description.
    # -b option rule applies the same as -a.
    # -c option doesn't require a value (the colon absense means that) and its existence should be set in C_VARIABLE
    #
    #   ~$ echo get_options ${OPT_DESC}
    #   a:b:c
    #   ~$
    #
    
    
    # Required options 
    REQUIRED_DESC="a:=>REQ_A_VAR_VALUE|B:=>REQ_B_VAR_VALUE|c=>REQ_C_VAR_FLAG"
    
    # Optional options (duh)
    OPTIONAL_DESC="P:=>OPT_P_VAR_VALUE|r=>OPT_R_VAR_FLAG"
    
    function usage {
        IFS="|"
        printf "%s" ${0}
        for i in ${REQUIRED_DESC};
        do
            VARNAME=$(echo $i | sed -e "s/.*=>//g")
        printf " %s" "-${i:0:1} $VARNAME"
        done
    
        for i in ${OPTIONAL_DESC};
        do
            VARNAME=$(echo $i | sed -e "s/.*=>//g")
            printf " %s" "[-${i:0:1} $VARNAME]"
        done
        printf "\n"
        unset IFS
        exit
    }
    
    # Auxiliary function that returns options characters to be passed
    # into 'getopts' from a option description.
    # Arguments:
    #   $1: The options description (SEE TOP)
    #
    # Example:
    #   OPT_DESC="h:=>H_VAR|f:=>F_VAR|P=>P_VAR|W=>W_VAR"
    #   OPTIONS=$(get_options ${OPT_DESC})
    #   echo "${OPTIONS}"
    #
    # Output:
    #   "h:f:PW"
    function get_options {
        echo ${1} | sed -e "s/\([a-zA-Z]\:\?\)=>[A-Z_]*|\?/\1/g"
    }
    
    # Auxiliary function that returns all variable names separated by '|'
    # Arguments:
    #       $1: The options description (SEE TOP)
    #
    # Example:
    #       OPT_DESC="h:=>H_VAR|f:=>F_VAR|P=>P_VAR|W=>W_VAR"
    #       VARNAMES=$(get_values ${OPT_DESC})
    #       echo "${VARNAMES}"
    #
    # Output:
    #       "H_VAR|F_VAR|P_VAR|W_VAR"
    function get_variables {
        echo ${1} | sed -e "s/[a-zA-Z]\:\?=>\([^|]*\)/\1/g"
    }
    
    # Auxiliary function that returns the variable name based on the
    # option passed by.
    # Arguments:
    #   $1: The options description (SEE TOP)
    #   $2: The option which the variable name wants to be retrieved
    #
    # Example:
    #   OPT_DESC="h:=>H_VAR|f:=>F_VAR|P=>P_VAR|W=>W_VAR"
    #   H_VAR=$(get_variable_name ${OPT_DESC} "h")
    #   echo "${H_VAR}"
    #
    # Output:
    #   "H_VAR"
    function get_variable_name {
        VAR=$(echo ${1} | sed -e "s/.*${2}\:\?=>\([^|]*\).*/\1/g")
        if [[ ${VAR} == ${1} ]]; then
            echo ""
        else
            echo ${VAR}
        fi
    }
    
    # Gets the required options from the required description
    REQUIRED=$(get_options ${REQUIRED_DESC})
    
    # Gets the optional options (duh) from the optional description
    OPTIONAL=$(get_options ${OPTIONAL_DESC})
    
    # or... $(get_options "${OPTIONAL_DESC}|${REQUIRED_DESC}")
    
    # The colon at starts instructs getopts to remain silent
    while getopts ":${REQUIRED}${OPTIONAL}" OPTION
    do
        [[ ${OPTION} == ":" ]] && usage
        VAR=$(get_variable_name "${REQUIRED_DESC}|${OPTIONAL_DESC}" ${OPTION})
        [[ -n ${VAR} ]] && eval "$VAR=${OPTARG}"
    done
    
    shift $(($OPTIND - 1))
    
    # Checks for required options. Report an error and exits if
    # required options are missing.
    
    # Using function version ...
    VARS=$(get_variables ${REQUIRED_DESC})
    IFS="|"
    for VARNAME in $VARS;
    do
        [[ -v ${VARNAME} ]] || usage
    done
    unset IFS
    
    # ... or using IFS Version (no function)
    OLDIFS=${IFS}
    IFS="|"
    for i in ${REQUIRED_DESC};
    do
        VARNAME=$(echo $i | sed -e "s/.*=>//g")
        [[ -v ${VARNAME} ]] || usage
        printf "%s %s %s\n" "-${i:0:1}" "${!VARNAME:=present}" "${VARNAME}"
    done
    IFS=${OLDIFS}
    

    I didn't test this roughly, so I could have some bugs in there.

    0 讨论(0)
提交回复
热议问题