Search code examples
linuxbashshellgetopts

getopts doesn't work when there are arguments before options


When I run the command like this :

$: ./script -r f1 f2 :
it detects the "-r" flag and sets the recursive flag to 1.

$: ./script directory/ -r :
getopts doesn't detect the -r flag at all. So inside the case statement it never detects -r flag and so the while loop doens't even run at all. how to fix this ?

RECURSIVE_FLAG=0
while getopts ":rR" opt ; do
    echo " opt = $opt"
    set -x
    case "$opt" in 

        r) RECURSIVE_FLAG=1 ;;
        R) RECURSIVE_FLAG=1 ;;
        :)echo "not working" ;;
        *)echo "Testing *" ;;

    esac
done

Solution

  • It has nothing to do with slash. getopts stops processing options when it gets to the first argument that doesn't begin with -. This is the documented behavior:

    When the end of options is encountered, getopts exits with a return value greater than zero. OPTIND is set to the index of the first non-option argument, and name is set to ?.

    Your claim that it works when you use

    ./script f1 f2 -r
    

    is simply wrong. I added echo $RECURSIVE_FLAG to the end of your script, and when I ran it that way it echoed 0.

    If you want to allow a more liberal syntax, with options after filenames (like GNU rm) you'll need to do some argument parsing of your own. Put your getopts loop inside another loop. When the getopts loop finishes, you can do:

    # Find next option argument
    while [[ $OPTIND <= $# && ${!OPTIND} != -* ]]; do
        ((OPTIND++))
    done
    # Stop when we've run out of arguments
    if [[ $OPTIND > $# ]]; then
        break
    fi