Using getopts inside a Bash function

Magnus picture Magnus · May 20, 2013 · Viewed 25.6k times · Source

I'd like to use getopts inside a function that I have defined in my .bash_profile. The idea is I'd like to pass in some flags to this function to alter its behavior.

Here's the code:

function t() {
    echo $*
    getopts "a:" OPTION
    echo $OPTION
    echo $OPTARG
}

When I invoke it like this:

t -a bc

I get this output:

-a bc
?
 

What's wrong? I'd like to get the value bc without manually shifting and parsing. How do I use getopts correctly inside a function?

EDIT: corrected my code snippet to try $OPTARG, to no avail

EDIT #2: OK turns out the code is fine, my shell was somehow messed up. Opening a new window solved it. The arg value was indeed in $OPTARG.

Answer

Adrian Frühwirth picture Adrian Frühwirth · May 20, 2013

As @Ansgar points out, the argument to your option is stored in ${OPTARG}, but this is not the only thing to watch out for when using getopts inside a function. You also need to make sure that ${OPTIND} is local to the function by either unsetting it or declaring it local, otherwise you will encounter unexpected behaviour when invoking the function multiple times.

t.sh:

#!/bin/bash

foo()
{
    foo_usage() { echo "foo: [-a <arg>]" 1>&2; exit; }

    local OPTIND o a
    while getopts ":a:" o; do
        case "${o}" in
            a)
                a="${OPTARG}"
                ;;
            *)
                foo_usage
                ;;
        esac
    done
    shift $((OPTIND-1))

    echo "a: [${a}], non-option arguments: $*"
}

foo
foo -a bc bar quux
foo -x

Example run:

$ ./t.sh
a: [], non-option arguments:
a: [bc], non-option arguments: bar quux
foo: [-a <arg>]

If you comment out # local OPTIND, this is what you get instead:

$ ./t.sh
a: [], non-option arguments:
a: [bc], non-option arguments: bar quux
a: [bc], non-option arguments:

Other than that, its usage is the same as when used outside of a function.