如何在bash中使用getopts的例子

我想以这种方式调用myscript文件:

 $ ./myscript -s 45 -p any_string 

要么

 $ ./myscript -h >>> should display help $ ./myscript >>> should display help 

我的要求是:

  • getopt在这里获取input参数
  • 检查-s存在,如果不是则返回错误
  • 检查-s之后的值是45还是90
  • 检查-p存在,之后是否有inputstring
  • 如果用户input./myscript -h或只./myscript则显示帮助

我试过这个代码:

 #!/bin/bash while getopts "h:s:" arg; do case $arg in h) echo "usage" ;; s) strength=$OPTARG echo $strength ;; esac done 

但是用这个代码,我得到错误。 如何用Bash和getopt做到这一点?

 #!/bin/bash usage() { echo "Usage: $0 [-s <45|90>] [-p <string>]" 1>&2; exit 1; } while getopts ":s:p:" o; do case "${o}" in s) s=${OPTARG} ((s == 45 || s == 90)) || usage ;; p) p=${OPTARG} ;; *) usage ;; esac done shift $((OPTIND-1)) if [ -z "${s}" ] || [ -z "${p}" ]; then usage fi echo "s = ${s}" echo "p = ${p}" 

示例运行:

 $ ./myscript.sh Usage: ./myscript.sh [-s <45|90>] [-p <string>] $ ./myscript.sh -h Usage: ./myscript.sh [-s <45|90>] [-p <string>] $ ./myscript.sh -s "" -p "" Usage: ./myscript.sh [-s <45|90>] [-p <string>] $ ./myscript.sh -s 10 -p foo Usage: ./myscript.sh [-s <45|90>] [-p <string>] $ ./myscript.sh -s 45 -p foo s = 45 p = foo $ ./myscript.sh -s 90 -p bar s = 90 p = bar 

原始代码的问题是:

  • h:期望不应该的参数,所以把它改成h (不用冒号)
  • 期望-p any_string ,您需要将p:添加到参数列表中

getopts的基本语法是(参见: man bash ):

 getopts OPTSTRING VARNAME [ARGS...] 

哪里:

  • OPTSTRING是带有预期参数列表的string,

    • h – 检查没有参数的选项-h ; 在不支持的选项上出错;
    • h:参数检查选项-h ; 在不支持的选项上给出错误;
    • abc – 检查选项-a-b-c ; 在不支持的选项上给出错误;
    • :abc – 检查选项-a-b-c ; 消除不支持的选项上的错误;

      注意:换句话说,在选项前面的冒号允许你处理你的代码中的错误。 variables将包含? 在不支持的情况下:在缺失值的情况下。

  • OPTARG – 设置为当前参数值,

  • OPTERR – 指示Bash是否应显示错误消息。

所以代码可以是:

 #!/usr/bin/env bash usage() { echo "$0 usage:" && grep " .)\ #" $0; exit 0; } [ $# -eq 0 ] && usage while getopts ":hs:p:" arg; do case $arg in p) # Specify p value. echo "p is ${OPTARG}" ;; s) # Specify strength, either 45 or 90. strength=${OPTARG} [ $strength -eq 45 -o $strength -eq 90 ] \ && echo "Strength is $strength." \ || echo "Strength needs to be either 45 or 90, $strength found instead." ;; h | *) # Display help. usage exit 0 ;; esac done 

用法示例:

 $ ./foo.sh ./foo.sh usage: p) # Specify p value. s) # Specify strength, either 45 or 90. h | *) # Display help. $ ./foo.sh -s 123 -p any_string Strength needs to be either 45 or 90, 123 found instead. p is any_string $ ./foo.sh -s 90 -p any_string Strength is 90. p is any_string 

请参阅:Bash Hackers Wiki上的小型getopts教程

getopt打包的例子(我的发行版放在/usr/share/getopt/getopt-parse.bash )看起来像涵盖了所有的情况:

 #!/bin/bash # A small example program for using the new getopt(1) program. # This program will only work with bash(1) # An similar program using the tcsh(1) script language can be found # as parse.tcsh # Example input and output (from the bash prompt): # ./parse.bash -a par1 'another arg' --c-long 'wow!*\?' -cmore -b " very long " # Option a # Option c, no argument # Option c, argument `more' # Option b, argument ` very long ' # Remaining arguments: # --> `par1' # --> `another arg' # --> `wow!*\?' # Note that we use `"$@"' to let each command-line parameter expand to a # separate word. The quotes around `$@' are essential! # We need TEMP as the `eval set --' would nuke the return value of getopt. TEMP=`getopt -o ab:c:: --long a-long,b-long:,c-long:: \ -n 'example.bash' -- "$@"` if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi # Note the quotes around `$TEMP': they are essential! eval set -- "$TEMP" while true ; do case "$1" in -a|--a-long) echo "Option a" ; shift ;; -b|--b-long) echo "Option b, argument \`$2'" ; shift 2 ;; -c|--c-long) # c has an optional argument. As we are in quoted mode, # an empty parameter will be generated if its optional # argument is not found. case "$2" in "") echo "Option c, no argument"; shift 2 ;; *) echo "Option c, argument \`$2'" ; shift 2 ;; esac ;; --) shift ; break ;; *) echo "Internal error!" ; exit 1 ;; esac done echo "Remaining arguments:" for arg do echo '--> '"\`$arg'" ; done 

我知道这已经回答了,但是为了logging,对于和我一样重要的人,我决定发表这个相关的答案。 代码充斥着注释来解释代码。

更新回答:

将文件另存为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 } 

那么你可以像这样使用它:

 #!/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} 

老答案:

我最近需要使用一种通用的方法。 我碰到这个解决scheme:

 #!/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} 

我没有粗略testing,所以我可能在那里有一些错误。