Search code examples
bashgetopts

How to prevent non-optarg args to Bash function?


I've got a Bash function for using a select statement with git checkout. It works fine. I'd like to prevent a use case, and I'm not sure how. How can I prevent non-optarg values being passed to the function as "$1". For example:

  1. $ gco hello should no-op since hello is not associated with a -b flag. <-- Need to implement
  2. $ gco -b hello should check out the branch hello. <-- Working
  3. $ gco should present the select menu. <-- Working

As expected, checking for "$1" isn't the answer because it messes with potential optargs.

    if [ -z "$1" ]; then
        echo "$1"
        return
    fi
gco () {
    local branch=""

    while getopts :hb: opt; do
        case "$opt" in
            h) 
                echo "NAME:     gco - git branch to check out."
                echo "SYNOPSYS: gco [-b branch]"
                echo "-h        Display help text."
                echo "-b        branch"
                return
                ;;
            b) branch="$OPTARG" ;;
            :) echo "Missing argument for option -$OPTARG"; return 1 ;;
           \?) echo "Unknown option -$OPTARG"; return 1 ;;
        esac
    done

    if [ "$branch" != "" ]; then
        git checkout "$branch"
        return
    fi

    echo "Which branch would you like to check out?"
    select b in $(git branch | sed 's/* /  /'); do
        git checkout "$b"
        return
    done
}

Solution

  • Typical for using getopts is to 'shift' the parsed options after getopts finished. So after your while loop you can do

     shift $((OPTIND - 1))
    

    to remove all parsed options. OPTIND indicates the next option getopts would check so after the loop it points to the first non-option. As example when calling your function with

    gco -b master
      # OPTIND is 3 after the while loop
    gco hello
      # OPTIND is 1 after the while loop
    

    Now you actually can check for $1 if they are more non-opt args or in your case it is probably enough to

    [[ $# == 0 ]] || return