52

Is it possible to use getopts to process multiple options together? For example, myscript -iR or myscript -irv.

Also, I have a situation where based on a condition script would need mandatory option. For example, if argument to script is a directory, I will need to specify -R or -r option along with any other options (myscript -iR mydir or myscript -ir mydir or myscript -i -r mydir or myscript -i -R mydir), in case of file only -i is sufficient (myscript -i myfile).

I tried to search but didn't get any answers.

Paul T.
  • 714
  • 1
  • 9
  • 20
Ramesh Samane
  • 585
  • 1
  • 6
  • 7

1 Answers1

129

You can concatenate the options you provide and getopts will separate them. In your case statement you will handle each option individually.

You can set a flag when options are seen and check to make sure mandatory "options" (!) are present after the getopts loop has completed.

Here is an example:

#!/bin/bash
rflag=false
small_r=false
big_r=false
    
usage () { echo "How to use"; }
    
options=':ij:rRvhm'
while getopts $options option
do
    case "$option" in
        i  ) i_func;;
        j  ) j_arg=$OPTARG;;
        r  ) rflag=true; small_r=true;;
        R  ) rflag=true; big_r=true;;
        v  ) v_func; other_func;;
        h  ) usage; exit;;
        \? ) echo "Unknown option: -$OPTARG" >&2; exit 1;;
        :  ) echo "Missing option argument for -$OPTARG" >&2; exit 1;;
        *  ) echo "Unimplemented option: -$option" >&2; exit 1;;
    esac
done

if ((OPTIND == 1))
then
    echo "No options specified"
fi

shift $((OPTIND - 1))

if (($# == 0))
then
    echo "No positional arguments specified"
fi
    
if ! $rflag && [[ -d $1 ]]
then
    echo "-r or -R must be included when a directory is specified" >&2
    exit 1
fi

This represents a complete reference implementation of a getopts function, but is only a sketch of a larger script.

Dennis Williamson
  • 346,391
  • 90
  • 374
  • 439
  • 1
    Many thanks Dennis. I have used the flags as you suggested. I tried to simplify the logic by concatenating options and storing them in a variable and later doing processing based on the options provided. – Ramesh Samane Jul 01 '12 at 09:14
  • 1
    The difference between unimplemented and unknown option is that for the former an option was supplied which matches an option character in `$options`, but no entry in the `case` statement and for the latter an option was supplied which doesn't match any character in `$options`. – Dennis Williamson May 28 '15 at 18:48
  • 4
    Note that you do not have to prefix variable names with `$` within arithmetic blocks; so you can just do `shift $((OPTIND-1))` – kbolino Dec 11 '15 at 18:39
  • 4
    By the way `* ) echo "Unimplemented option: -$OPTARG" >&2; exit 1;;` should state `-$option` and not `-$OPTARG` otherwise it says the _argument_ to the option is unimplemented rather than the _flag_ is unimplemented. The original way using `-$OPTARG`: ` ./foo.bash -m foo Unimplemented option: -foo ` If you use `-$option` you get what's expected: ` ./foo -m foo Unimplemented option: -m ` – Elven Spellmaker Jul 28 '20 at 18:25
  • @ElvenSpellmaker: Did you test it? Did you read the documentation? "If the first character of optstring is a colon, silent error reporting is used." and "If getopts is silent, then a colon (:) is placed in name and _OPTARG is set to the option character found_." [emphasis mine] – Dennis Williamson Jun 28 '22 at 16:26
  • @DennisWilliamson Taking your exact code produces exactly what I said on bash. Just add `m` to the list of options and then run the script specifying the `-m` flag. `$ bash /tmp/foo.bash -m foo` `Unimplemented option: -` – Elven Spellmaker Jun 28 '22 at 20:12
  • 1
    @ElvenSpellmaker: I apologize. You are correct. I've edited my answer. – Dennis Williamson Jun 29 '22 at 15:16
  • Nice one, thanks for editing. – Elven Spellmaker Jun 29 '22 at 19:10