Search code examples
bashgetopt

Why `$OPTIND` can't exist in a function containing getopt?


Save the following code as testme.sh.

OPTS=$(getopt -o a:b:c -- "$@")
eval set -- "$OPTS"
while true; do
    case "$1" in
        -a  )                 
            echo  "i am in a",$OPTIND
            shift 2;;
        -b  ) 
            echo  "i am in b",$OPTIND
            shift 2;;
        -c ) 
            echo  "i am in c",$OPTIND
            shift;;
        -- ) 
            shift;; 
        *)
            break;;
    esac
done

Run it with bash /tmp/testme.sh -a a1 -b b1 -c.

i am in a,1
i am in b,1
i am in c,1

Now wrap all content in testme.sh as a function.

testme(){
OPTS=$(getopt -o a:b:c -- "$@")
eval set -- "$OPTS"
while true; do
    case "$1" in
        -a  )                 
            echo  "i am in a",$OPTIND
            shift 2;;
        -b  ) 
            echo  "i am in b",$OPTIND
            shift 2;;
        -c ) 
            echo  "i am in c",$OPTIND
            shift;;
        -- ) 
            shift;; 
        *)
            break;;
    esac
done
}

Run it with testme -a a1 -b b1 -c.

i am in a,
i am in b,
i am in c,

There are 2 issues confused me.

1.Why all $OPTINDs value is 1 when to run bash /tmp/testme.sh -a a1 -b b1 -c ?

2.Why no $OPTINDs value at all when to run testme -a a1 -b b1 -c ?


Solution

  • getopt is an external command and runs in a subprocess, so it can't modify the original shell's variables. Because of this, it can't set variables like OPTIND and OPTARG, the way the built-in command getopts does. It simply outputs a modified version of the argument list, which can be assigned to positional parameters with set.

    So when you use getopt rather than getopts, $OPTIND is not updated. It's initialized to 1 when a shell script starts. Since your testme.sh script never does anything that updates the variable, you get 1 every time through the loop.

    When I try your testme function, I also see 1 every time. If you're not seeing this, you must have reassigned OPTIND before running the function. Try:

    OPTIND=123
    testme -a a1 -b b1 -c
    

    and you should see

    i am in a,123
    i am in b,123
    i am in c,123