Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

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).
  • Support of short options including concatenated options (-a -b -c / -abc).
  • No need to support single hyphen long options (i.e. find -name ...)
  • 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.

Awk looks like a good candidate for implementing this but have been unable to find a solution, in awk or any other language, that meets these requirements.

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

share|improve this question
    
Have you looked at shflags? –  rush Feb 18 '13 at 9:15
    
Thanks for the info! Just checked it out. Some interesting things there but still pretty involved. –  DocSalvage Feb 18 '13 at 10:07
    
I've used a few different solutions, but eventually just settled for getopts. I had to accept it's limitations, but it's a POSIX shell standard, so it felt like a worthwhile trade off. I also avoid using shell loops, and agree they are generally inefficient, but I just bit the bullet for getopts. I still might be able to point you in a solid direction. What are your goals? Efficiency? Portability? Features? Simplicity? Which are your willing to compromise, to achieve the others? –  TechZilla Feb 18 '13 at 21:00
    
5min limit so had to add new comment... Thank you for jumping in! The most important goal would be simplicity/clarity of code using the solution. The function(s) could be fairly complex (use awk, sed, regex's, etc.) if they are "black boxes" that make the calling code easier to work with. Next would be "quality" of the function code which I define as a combination of consistency, symmetry and what we used to call "elegance". Third, would be efficiency, followed by features and portability to shells other than bash. –  DocSalvage Feb 18 '13 at 21:36
add comment

1 Answer

up vote 0 down vote accepted

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   "$@")"
share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.