Getopt没有很好地解析bash

时间:2016-07-11 12:42:50

标签: bash arguments getopt

我在Bash中编写了一个脚本,作为多个监视器的模板。我选择getopt是为了能够在CLI上使用长选项。但是,我在正确实现它时遇到了一些问题。

整个脚本要长得多,但这是相关部分:

#!/bin/bash
#
# FUNCTION
#   main
# DESCRIPTION
#   Main function. Everything will be called from here
# ARGS
#   Nothing
#
# RETURN CODE
#   Nothing
#
# Main function. Everything will be called from here
main() {
  # Parse the options and arguments
  parse_options "${@}"

  # Check if the interval is set to a valid number
  check_interval
}

#
# FUNCTION
#   check_interval
# DESCRIPTION
#   Checks if a number is valid
# ARGS
#   1: number to be checked
# RETURN CODE
#   0: valid
#   1: invalid
#
check_interval() {
  # We don't have to worry if interval is set at all, because getopt is already doing this
  if ( ! check_number_pos ${arginterval} ); then
    echo "Error: invalid interval: ${arginterval}"
    show_usage
    exit 2
  fi
}

#
# FUNCTION
#   show_usage
# DESCRIPTION
#   This is the Usage section. We a showing the Usage according to docopt standards
# ARGS
#   Nothing
# RETURN CODE
#   Nothing
#
show_usage() {
  echo "Usage:"                                                                           >&2
  echo "  ${THIS_SCRIPT_NAME} -i|--interval=<interval in s> [-r | --random [--randomwait=<wait in s>]] [-v|--verbose] [-d|--debug] [--colors]"  >&2
  echo "  ${THIS_SCRIPT_NAME} [-h|--help]"                                                >&2
}

#
# FUNCTION
#   check_number_pos
# DESCRIPTION
#   Checks if a number is valid and positive
# ARGS
#   1: number to be checked
#
# RETURN CODE
#   0: valid and positive
#   1: invalid or negative
#
check_number_pos() {
  local returnval
  if [[ "${1}" =~ ^[0-9]+$ ]]; then
    returnval=0
  else
    returnval=1
  fi
  return ${returnval}
}

#
# FUNCTION
#   parse_options
# DESCRIPTION
#   Parse options from command line
# ARGS
#   @: Arguments and options as given at CLI
# RETURN CODE
#   Nothing
#
parse_options() {
  # Use getopt(1) to parse options according to POSIX. If it fails, an error is shown, and we're showing the Usage and exit
  # Add new options here and also in the case-statement below.
  # Short options must be added in the 'options'-section
  # Long options must be added in the 'longoptions'-section
  # All short options must have a long equivalent
  # The --name is set so the error-output will not show 'getopt'-errors but neat <application name>-errors
  # Options and longoptions have the following format:
  # <letter>       Option without argument
  # <letter>:      Option with mandarory argument
  # <letter>::     Option with optional argument <- this is broken for short options and long options without '='. Don't use it!
  local -r GETOPT=$(getopt --name ${0} --options hrvdi: --longoptions help,random,verbose,debug,colors,randomwait:,interval: -- "${@}")

  if [ ${?} != 0 ]; then
    echo "Error: Error while getting arguments"
    show_usage
    exit 127;
  fi

  # No options or arguments given. Show Usage.
  if [[ "${GETOPT}" == " --" ]]; then
    show_usage
    exit 127;
  fi

  # Evaluate GETOPT. We need this to have the quotes in the output of getopt(1) interpreted.
  eval set -- "${GETOPT}"

  # Walk through all the options. Don't put too much code in here, just point to a function or set a variable.
  # Please note, all new options need to be added here but also in the GETOPT line above.
  # Note: shift removes the first value from the string, so the option itself will be removed from the GETOPT-string, and the argument is available in $1
  # After using an argument, please shift again, so the next option will be the first value in GETOPT
  while true;
  do
    case "${1}" in
      -i|--interval)
        shift
        arginterval=${1}
        shift
        ;;
      -r|--random)
        shift
        flagrandom=1
        ;;
      --randomwait)
        shift
        flagrandom=1
        argrandom=${1}
        shift
        ;;
      -v|-d|--verbose|--debug)
        flagdebug=1
        shift
        ;;
      --colors)
        flagcolors=1
        shift
        ;;
      -h|--help)
        #show_help
        exit 0
        ;;
      --)
        shift
        break
        ;;
      -*)
    echo "Error: unrecognized option ${1}"
        show_usage
        exit 127
        ;;
      *)
        show_usage
        exit 127
        ;;
    esac
  done
}

#Call main function after all
main "${@}"

现在,当我以正确的方式调用脚本时,一切顺利:

$ ./test1.sh -i 10

当我忘记这个论点时,它也会做我想要的:

$ ./test1.sh -i
./test1.sh: option requires an argument -- 'i'
Usage:
   -i|--interval=<interval in s> [-r | --random [--randomwait=<wait in s>]] [-v|--verbose] [-d|--debug] [--colors]
   [-h|--help]

但是当我只是忘记了这个论点并添加了另一个时,它就失败了:

$ ./test1.sh -i --colors
Error: invalid interval: --colors
Usage:
   -i|--interval=<interval in s> [-r | --random [--randomwait=<wait in s>]] [-v|--verbose] [-d|--debug] [--colors]
   [-h|--help]

这是因为我检查间隔是否是整数,但是出于其他目的,这是一个危险的事情我如何更改案例以便它不会将选项作为参数读取?到目前为止,getopt并不能很好地为我服务,因为我也遇到了另一个错误/特性:'可选参数'(::)不能正常工作,因为它只适用于'='之间的选项和论点。

版本:

$ getopt -V
getopt (enhanced) 1.1.4
$ bash --version
GNU bash, version 4.1.2(1)-release (x86_64-redhat-linux-gnu)

1 个答案:

答案 0 :(得分:3)

getopt并不了解您的选项的语义。尽管如此,--colors-i选项的有效参数。不幸的是,如果你想处理这些错误,你必须自己检查这些错误。

 while true;
  do
    case "${1}" in
      -i|--interval)
        shift
        arginterval=${1}
        if [[ $arginterval = -* ]]; then
            printf 'You appear to have forgotten the interval argument before the %s option\n' "$arginterval" >&2
            exit 1
        fi
        shift
        ;;

    ...