1

I'm having trouble wrapping my head around how to make getopts accept multiple flags with a single argument. For example.

Flags: Children: a - Adam b - Beth c - Cathy d - Duncan

Required parameter: ice cream flavor

(not what my script really does, but easiest to spell out this way)

What I can do right now:

./flavor -a chocolate ./flavor -c chocolate 

What I'd like to do:

./flavor -acd chocolate 

Rather than the only thing I can get it to do so far:

./flavor -a chocolate -c chocolate -d chocolate 

Every variation I've tried with getopts either doesn't see the parameter at all or requires it after every single flag declaration. I feel like I'm missing something obvious that will give me the "aha" moment, but I'm not seeing it on my own. Can anyone point me in the right direction?

============================================

while getopts ":a:b:c:d:" opt; do case $opt in a) do stuff with $OPTARG ;; b) do stuff with $OPTARG ;; c) do stuff with $OPTARG ;; d) do stuff with $OPTARG ;; :) echo "Option -$OPTARG requires an argument." >&2 exit 1 ;; esac done 

============================================

My apologies if this is a really dumb question and thank you for your time.

2
  • You aren't missing anything; there isn't a way to specify that a single argument should be shared by all the options in a group. Commented May 23, 2016 at 17:18
  • I believe you can't do that, but I may be wrong. With ./flavor -acd a will consume cd as its $OPTARG. Commented May 23, 2016 at 17:19

2 Answers 2

1

Stacking options that require arguments is not supported by getopts. You can only stack options that do not take arguments.

To do what you propose, a lot more complication in the argument processing is required. Essentially you would have to pre-process the options to find out where the non-option arguments begin. This is not particularly beautiful or recommended, but this does literally what you asked for without requiring an additional option:

_GETOPTS=":abcd" while getopts "${_GETOPTS}" opt do case $opt in abcd) : ;; \?) echo "Unsupported option (-${OPTARG})" >&2 exit 1 ;; esac done eval "FIRSTARG=\${${OPTIND}}" if [ -z "${FIRSTARG}" ] then echo "Flavor argument required." exit 1 fi OPTIND=1 while getopts "${_GETOPTS}" opt do case $opt in a) echo do a stuff with ${FIRSTARG} ;; b) echo do b stuff with ${FIRSTARG} ;; c) echo do c stuff with ${FIRSTARG} ;; d) echo do d stuff with ${FIRSTARG} ;; esac done 

Results:

$ ./flavor -acd chocolate do a stuff with chocolate do c stuff with chocolate do d stuff with chocolate 

One can access other arguments after the first non-option argument, but that would take a bit more work.

Note that handling the same set of options in multiple getopts statements is prone to create long term maintenance issues in that it might be too easy for the options handled by the two case statements to get out of sync.

Sign up to request clarification or add additional context in comments.

3 Comments

Thank you. This gets me about 90% of the way there. I just have to figure out how to make the parameter required now so that it can't be left out.
Insert just before the second while statement something along the lines of if [ -z "${FIRSTARG}" ]; then echo Flavor argument required; exit 1; fi
Yep. Works like a charm! Thanks again. :)
0

A bit tweaky way of doing it would be :

 #!/bin/bash while getopts ":abcdf:" opt; do case $opt in f) FLAVOUR="$OPTARG" if [ ${ADAM_IN:-0} -eq 1 ] then echo "Adam gets $FLAVOUR" ADAM_IN=0 fi if [ ${BETH_IN:-0} -eq 1 ] then echo "Beth gets $FLAVOUR" BETH_IN=0 fi if [ ${CATHY_IN:-0} -eq 1 ] then echo "Cathy gets $FLAVOUR" CATHY_IN=0 fi if [ ${DUNCAN_IN:-0} -eq 1 ] then echo "Duncan gets $FLAVOUR" DUNCAN_IN=0 fi ;; a) ADAM_IN=1 ;; b) BETH_IN=1 ;; c) CATHY_IN=1 ;; d) DUNCAN_IN=1 ;; :) echo "invalid argument" 2>/dev/null exit 1 ;; esac done 

Save the script as ice_cream_allocator and run it like:

$ ./ice_cream_allocator -abcf"Chocolate" -df"Vanila" -cf"Strawberry" 

Output

Adam gets Chocolate Beth gets Chocolate Cathy gets Chocolate Duncan gets Vanila Cathy gets Strawberry 

Well I have a soft-corner for Cathy so giving her two flavours ;)

2 Comments

I see what you're doing here, but when I de-simplify what my script is actually doing (ie, not ice cream), this won't be feasible. I do thank you for taking the time to reply and offer assistance.
@Mathaen : You're welcome, and good luck with what you wish to do. :-)

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.