2

I'm trying to get a script to:

  • set a variable with -q option
  • show help for -h option, and
  • fail for other options -*, but allow positional arguments

Here is the getopts snippet I'm using:

while getopts qh opt; do
    case "${opt}" in
        q)
            quiet="true"
            ;;
        h)
            usage
            exit 1
            ;;
        \?)
            echo "unrecognized option -- ${OPTARG}"
            exit 1
            ;;
    esac
    shift
done

echo "unparsed: $*"

This seems pretty straightforward. However it only works if I provide a single argument (a.sh -q or a.sh -h do what's expected).

However, it does not do anything if I provide both arguments, or provide an unrecognized argument as at $2:

$ ./a.sh -b
unrecognized option -- b

$ ./a.sh -q -b
unparsed: -b

$ ./a.sh -h -k
this is my help message
unparsed: -k

Any ideas why the second argument ($2) is not handled in the getopts loop?

4

The shift command is misplaced. It should be outside of the while loop. Try:

while getopts :qh opt; do
    case "${opt}" in
        q)
            quiet="true"
            ;;
        h)
            usage
            exit 1
            ;;
        \?)
            echo "unrecognized option -- ${OPTARG}"
            exit 1
            ;;
    esac
done
shift $((OPTIND - 1))

echo "unparsed: $*"

Examples

If we add the following line to the beginning of the code:

usage() { echo "this is my help message"; }

Then we can do these tests:

$ ./a.sh -q -foo
unrecognized option -- f
$ ./a.sh -q -h
this is my help message
  • Doesn't help. ./a.sh -q -foo still doesn't hit unrecognized option block. Also -q -h doesn't hit h block either. – Ahmet Alp Balkan Oct 8 '17 at 7:17
  • @AhmetAlpBalkan Both work for me. See the examples shown in my updated answer. If you see something different, then please copy-and-paste my exact code to a script and try again. (If you are using some other code not shown here, then I don't know what's in it and I can't help.) – John1024 Oct 8 '17 at 7:32
  • Thanks it worked on a second try. What's the reason this works and shifting it one by one inside the loop doesn't? – Ahmet Alp Balkan Oct 8 '17 at 15:41
  • 1
    @AhmetAlpBalkan Very good. When the shifting is done inside the loop, getopts gets confused and loses track of its position. To give getopts a chance to process all the arguments, we need to wait to do the shifting outside the loop after getopts is finished, – John1024 Oct 8 '17 at 17:15

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.