Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using getopts to read one optional parameter and shift the right number of arguments

Tags:

bash

I'm writing a simple bash script that takes one optional parameter (-t ) followed by some number of additional arguments.

I was thinking getopts would be a reasonable way to achieve this, but I'm having difficulty getting the desired behavior. What I have is the following:

foo() {
    baz=10
    while getopts ":t:" option; do
        case "$option" in
        t) baz=$OPTARG ;;
        esac
    done
    shift $((OPTIND - 1))
    bar -t $baz $1 $2 $3
}

The problem is that $OPTIND does not seem to vary depending on whether the argument is present, so I don't get the correct behavior as expected with an optional parameter (i.e., I can't get shift to shift the right number of arguments regardless of whether the argument is there).

I want both of the following to execute correctly:

foo a b c
foo -t 5 a b c

What's the easiest way to achieve this? I'd prefer a solution that isn't a hack since I may want to use additional optional parameters.

like image 672
jonderry Avatar asked Feb 01 '13 22:02

jonderry


2 Answers

The problem is that you're never resetting $OPTIND, so each time you call foo it examines its arguments starting after the last processed option-index. For example:

# $OPTIND is now 1
foo -t opt arg1 arg2 arg3
# the above recognizes -t opt as an option, and sets $OPTIND to 3

# $OPTIND is now 3
foo arg4 arg5 arg6
# the above recognizes that arg6 is not an option, so it leaves $OPTIND at 3

The solution is to localize $OPTIND within foo, explicitly setting it to 1:

foo() {
    baz=10
    local OPTIND=1
    while getopts ":t:" option; do
        case "$option" in
        t) baz=$OPTARG ;;
        esac
    done
    shift $((OPTIND - 1))
    bar -t $baz $1 $2 $3
}

(You probably want to localize $baz as well, while you're at it.)

like image 102
ruakh Avatar answered Nov 13 '22 22:11

ruakh


It appears that you're trying to write a bash function, not a bash script. OPTIND is set to 1 when the shell/script is invoked, but not when a function is invoked, so subsequent function calls with different arguments would continue parsing at the same point.

If you want to keep it as a function, you can reset it manually:

foo() {
    OPTIND=1
    baz=10
    while getopts ":t:" option; do
        case "$option" in
        t) baz=$OPTARG ;;
        esac
    done
    echo $OPTIND
    shift $((OPTIND - 1))
    echo bar -t $baz $1 $2 $3
}
like image 35
that other guy Avatar answered Nov 13 '22 23:11

that other guy