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?

1 Answer 1

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
4
  • Doesn't help. ./a.sh -q -foo still doesn't hit unrecognized option block. Also -q -h doesn't hit h block either. Commented Oct 8, 2017 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
    Commented Oct 8, 2017 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? Commented Oct 8, 2017 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
    Commented Oct 8, 2017 at 17:15

You must log in to answer this question.

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