I have bash script I only want to support long options ("--option"). An --option can optionally have one or more arguments. All words (anything delimited by whitespace) up to but not including the first '--' OR the end of the command line string are considered "--option-arguments". A trailing space in the result is okay. Maximum performance is desired since this function is called by other functions for each possible option. Thus, trying to avoid bash loops and external commands.

Fought for many hours with the "first occurrence" issue until I found this answer that reminded me that POSIX (and thus bash) does not support nongreedy/lazy regex operators.

What to do?

  • Why use regexs here at all? A simple test for ${1#--} != $1 will do the trick. You can extract the arg name with ${1#--}. Speaking of efficiency, it's far better to loop through all arguments over a big case ...esac switch. In your case, you'd have a double case..esac. One to parse the argument names, and another to find the arguments.
    – Otheus
    Commented Feb 15, 2016 at 14:52
  • Thank you @Otheus. I wish to avoid for loops as they are quite slow in bash. Letting the C code within bash do iterations performs much better. Regex and Parameter Expansion accomplish that here. If I only needed to do this at script startup (the usual case), this would not be worth the effort. But this function is called by any other function that takes options (of which there are many) so performance is important. Commented Feb 15, 2016 at 16:46
  • 1
    You're building this for functions as well...? Hrm. The whole idea of iteratively calling the function for each parameter seems wasteful to me, but you might be right: the looping in interprative code is slower than regex in C
    – Otheus
    Commented Feb 15, 2016 at 22:06
  • Is there any reason why you can't or don't want to use /usr/bin/getopt from the util-linux package? It supports short and/or long options. The bash built-in getopts only supports short options, so you need to either write your own option processing or use getopt if you want long options. IMO unless you are on a system where /usr/bin/getopt is not available, you are much better off just using that....reinventing the wheel almost invariably ends up with something triangular in shape rather than round and missing important features.
    – cas
    Commented Feb 16, 2016 at 2:56
  • @cas ... In addition to avoiding external programs due to performance issues, getopt is designed to be used with a case statement which works best for mutually exclusive and independent options. Case statements almost always involve setting variables in order to use values repeatedly ("persistence"?). That gets messy. Commented Feb 16, 2016 at 4:08

2 Answers 2


You cannot parse options with regular expressions the way you seem to want because options are not passed in a string, but in a list of strings. myscript --option foo bar -- qux has myscript, --option, foo, bar, -- and qux as separate arguments, none of them containing whitespace.

A loop is the way to go in bash.

case "$1" in
    while [[ $# -ne 0 && "$1" != "--" ]]; do
    (($# == 0)) || shift

If performance is that much of a problem, you shouldn't be using bash. Try ksh instead — it's free, available pretty much everywhere even if it isn't installed by default, and it's often significantly faster than bash. If that's still too slow, you need a fancier programming language such as Perl, Python or Ruby.


Found this fairly simple solution...

function optionArg () {
  local _find="$1"; shift 1
  local _optarg=""
  local _reBeg=""
  _reBeg="${_find}"'[= ]+(.*?)( --)?'
  ### no regex nongreedy operator support in POSIX
  ### will have to just truncate after first match
  if [[ "$*" =~ $_reBeg ]]
    ### all arguments following --option[= ]
    ### limit to just arguments up to next --option (no lazy support in POSIX)
    return 0
    return 1

Given a script or function call with options followed by other options such as...

otherfunction --option1 arg1 arg2 --option2 -- file1 /home/me/file2

For each option accepted by otherfunction(), optionArg() would be called like...

_optarg1="$(optionArg --option1 "$@")"
_optarg2="$(optionArg --option2 "$@")"
_optarg3="$(optionArg --        "$@")"

The results would be...

_optarg1="arg1 arg2 "
_optarg3="file1 /home/me/file2"
  • This doesn't work (except in some specialized contexts where arguments can't meaningfully contain whitespace) because you're erasing the distinction between separate arguments and arguments containing whitespace. As a rule of thumb, if you're using $*, you're doing it wrong. Commented Feb 16, 2016 at 1:25
  • A good point @Gilles. But not a problem in this use case. Commented Feb 16, 2016 at 3:23
  • To whoever down-voted... Please consider not down-voting everything you disagree with. Just because you don't understand the logic, doesn't make it wrong. Please limit down-votes to grossly misleading approaches. I've been writing shell scripts for over 30 years in many different shells as well as programming in about 40 different languages including C and assembly. My low rep here is because I seldom post... which is because every time I do, someone more concerned with quick answers than thinking through problems starts down-voting. Commented Feb 16, 2016 at 3:30

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .