What would the best approach be to parse argument value ranges using getopts? As an example:

$ script.sh -r2-4,6,8-10

In my script I would then have an array with the values 2, 3, 4, 6, 8, 9 and 10.

  • 1
    I have no experience with getopts, but if you're using Bash, why not expand the range before, e.g. {2..4}? – slhck Dec 27 '12 at 10:17
  • Never thought of that, it works very well. One issue is that I have to use multiple options: -r{2..4} -r6 -r{8..10}. – c0dem4gnetic Dec 27 '12 at 10:26
  • @Bhargav Bhat You linked to the wrong getopt(3), which is not the same as getopt(1). – slhck Dec 27 '12 at 10:41
  • @slhck: oops! is corrected now. – Bhargav Bhat Dec 27 '12 at 10:43

If you want to both use -r...,.. and -r... -r... -r... : create a script such as this "toto.bash" :


    add_ranges () #this will add each integers or ranges (a-b) into the array: myarray
       #first we test that we only have those validchars (makes the second step below much easier to write)
       echo "$@" | grep "${validchars}" >/dev/null && {
          : ; #only valid chars, probably ok. (but could be wrong, ex: 1-  2,, 4-3  etc...)
       } || {
          echo "The ranges you entered are not valid : they should only contain such characters: ${validchars}"
          exit 1 ;
       #secondly we now try to handle the current ranges lists (comma separated)
       for range in $(echo "${@}" | tr ',' ' ')
          if   [[ ${range} =~ ^[0-9]+$ ]]     # [[ can handle regexp matching, and doesn't need to quote parameters
             myarray[${#myarray[*]}]=${range}  #add this after the latest element in myarray
          elif [[ ${range} =~ ^[0-9]+-[0-9]+$ ]]
             newrange=$(echo "$range" | sed -e 's/-/../')
             for i in `eval echo {$newrange}` # {a..b} means: all integers a to b
                myarray[${#myarray[*]}]=${i}  #add this after the latest element in myarray
             echo "ERROR: I can not recognize this range: $range"
             exit 1

   ###### handle options using getopts:
   OPTIND=1; #here for compatibility's sake: in case you add another function that uses getopts, reminds you to re-set OPTIND each time.
   while getopts "r:" zeoption; do
      case $zeoption in
            add_ranges "${OPTARG}";
        echo "option --, OPTARG=$OPTARG";
            break ;
        echo "ERROR: Unrecognized option: zeoption=$zeoption OPTARG=$OPTARG";

   shift $((OPTIND-1)) ; #we shift away all the options we processed.
   ###### end of options handling

    # and we continue here...

   echo "we now have : remaining arguments: ${@}"
   echo "and myarray contains: ${myarray[@]}"

and then run it:

$ ./toto.bash -r 2,4,6-12 -r 100-103 foo bar
we now have : remaining arguments: foo bar
and myarray contains: 2 4 6 7 8 9 10 11 12 100 101 102 103

I wanted to give just hints, but foudn out that writing it out is better. This makes for a long answer, but I hope it helps!

  • Brilliant! Thank you for adding comments in the script to make it easier to follow! – c0dem4gnetic Dec 27 '12 at 13:20
  • you're welcome. Some parts may need comments, so if you have questions about the way I wrote this or that, please do ask. – Olivier Dulac Dec 27 '12 at 13:28
  • I corrected the "elif" so that it closely match what we expect (otherwise it could contain additionnal unwanted characters before and after) – Olivier Dulac Dec 27 '12 at 13:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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