Getopt 解析不正确 bash

Getopt not parsing well bash

我在 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 [[ "" =~ ^[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 [=11=] --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 
  # After using an argument, please shift again, so the next option will be the first value in GETOPT
  while true;
  do
    case "" in
      -i|--interval)
        shift
        arginterval=
        shift
        ;;
      -r|--random)
        shift
        flagrandom=1
        ;;
      --randomwait)
        shift
        flagrandom=1
        argrandom=
        shift
        ;;
      -v|-d|--verbose|--debug)
        flagdebug=1
        shift
        ;;
      --colors)
        flagcolors=1
        shift
        ;;
      -h|--help)
        #show_help
        exit 0
        ;;
      --)
        shift
        break
        ;;
      -*)
    echo "Error: unrecognized option "
        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 对我的服务不是很好,因为我也 运行 进入另一个 bug/feature: 'optional argument' (::) 没有像我预期的那样工作,因为它只在使用时工作在选项和参数之间有一个“=”。

版本:

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

getopt 不知道您的选项的语义。就其所知,--colors-i 选项的有效参数。不幸的是,如果你想处理它们,你必须自己检查这些类型的错误。

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

    ...