Shell Script Options – Simpler Processing of Shell Script Options

bashcommand linegetoptsoptions

I'm looking for way to process shell script arguments that is cleaner and more "self documenting" than getopt/getopts.

It would need to provide…

  • Full support of long options with or without a value after '=' or ' '(space).
  • Proper handling of hyphenated option names (i.e. –ignore-case)
  • Proper handling of quoted option values (i.e. –text "A text string")

I would like to eliminate the overhead of the big loop with embedded case statement that getopt/getopts requires and reduce option processing to something like…

option=argumentparse "$@"
[[ option == ""           ]] && helpShow
[[ option =~ -h|--help    ]] && helpShow
[[ option =~ -v|--version ]] && versionShow
[[ option =~ -G|--GUI     ]] && GUI=$TRUE
[[ option =~ --title      ]] && TITLE=${option["--title"]}

Here, an argumentparse() function resolves the various syntax possibilities into a consistent format, perhaps an associative array.

There must be something coded out there somewhere. Any ideas?

(updated and retitled)

Best Answer

Since this question has been viewed so much (for me at least) but no answers were submitted, passing on the solution adopted...

NOTE
Some functions, like the multi-interface output functions ifHelpShow() and uiShow() are used but not included here as their calls contain relevant information but their implementations do not.

###############################################################################
# FUNCTIONS (bash 4.1.0)
###############################################################################

function isOption () {
  # isOption "$@"
  # Return true (0) if argument has 1 or more leading hyphens.
  # Example:
  #     isOption "$@"  && ...
  # Note:
  #   Cannot use ifHelpShow() here since cannot distinguish 'isOption --help'
  #   from 'isOption "$@"' where first argument in "$@" is '--help'
  # Revised:
  #     20140117 docsalvage
  # 
  # support both short and long options
  [[ "${1:0:1}" == "-" ]]  && return 0
  return 1
}

function optionArg () {
  ifHelpShow "$1" 'optionArg  --option "$@"
    Echo argument to option if any. Within "$@", option and argument may be separated
    by space or "=". Quoted strings are preserved. If no argument, nothing echoed.
    Return true (0) if option is in argument list, whether an option-argument supplied
    or not. Return false (1) if option not in argument list. See also option().
    Examples:
        FILE=$(optionArg --file "$1")
        if $(optionArg -f "$@"); then ...
        optionArg --file "$@"   && ...
    Revised:
        20140117 docsalvage'  && return
  #
  # --option to find (without '=argument' if any)
  local FINDOPT="$1"; shift
  local OPTION=""
  local ARG=
  local o=
  local re="^$FINDOPT="
  #
  # echo "option start: FINDOPT=$FINDOPT, o=$o, OPTION=$OPTION, ARG=$ARG, @=$@" >&2
  #
  # let "$@" split commandline, respecting quoted strings
  for o in "$@"
  do
    # echo "FINDOPT=$FINDOPT, o=$o, OPTION=$OPTION, ARG=$ARG" >&2
    # echo " o=$o"  >&2
    # echo "re=$re" >&2
    #
    # detect --option and handle --option=argument
    [[ $o =~ $re ]]  && { OPTION=$FINDOPT; ARG="${o/$FINDOPT=/}"; break; }
    #
    # $OPTION will be non-null if --option was detected in last pass through loop
    [[ ! $OPTION ]]  && [[ "$o" != $FINDOPT ]]   && {              continue; } # is a positional arg (no previous --option)
    [[ ! $OPTION ]]  && [[ "$o" == $FINDOPT ]]   && { OPTION="$o"; continue; } # is the arg to last --option
    [[   $OPTION ]]  &&   isOption "$o"          && {                 break; } # no more arguments
    [[   $OPTION ]]  && ! isOption "$o"          && { ARG="$o";       break; } # only allow 1 argument
  done
  #
  # echo "option  final: FINDOPT=$FINDOPT, o=$o, OPTION=$OPTION, ARG=$ARG, @=$@" >&2
  #
  # use '-n' to remove any blank lines
  echo -n "$ARG"
  [[ "$OPTION" == "$FINDOPT" ]]   && return 0
  return 1
}

###############################################################################
# MAIN  (bash 4.1.0) (excerpt of relevant lines)
###############################################################################

# options
[[ "$@" == ""            ]]   && { zimdialog --help           ; exit 0; }
[[ "$1" == "--help"      ]]   && { zimdialog --help           ; exit 0; }
[[ "$1" == "--version"   ]]   && { uiShow "version $VERSION\n"; exit 0; }

# options with arguments
TITLE="$(optionArg --title  "$@")"
TIP="$(  optionArg --tip    "$@")"
FILE="$( optionArg --file   "$@")"
Related Question