Shell Script – Assign $@ to Another Variable in Bash Function

arraybashshell-scriptzsh

I'm making an intelligent alias gc that can differentiate git commit/checkout.

If gc is called without any arguments or with the -a, -m arguments, then git commit is run. Otherwise, git checkout is run (if there is a b flag with an additional argument). If any other variation of gc is called, I'd prefer it throws an error rather than doing something unexpected.

Here is my shell function so far.

gc() {
    args=$@
    commit=false
    checkout=false

    # Check flags
    while getopts ":am:b" opt; do
        case $opt in
            a|m)
                commit=true
                ;;
            b)
                checkout=true
                ;;
            \?)
                echo "Unknown flags passed."
                return 1
                ;;
        esac
    done

    shift "$((OPTIND-1))"

    # Check number of arguments
    if [[ "$#" == 0 ]]; then
        commit=true
    elif [[ "$#" == 1 ]]; then
        checkout=true
    else
        echo "Too many arguments"
        return 1
    fi

    # Finally run respective command
    if [[ $commit == true && $checkout == true ]]; then
        echo "Unable to ascertain which operation you would like to perform."
        return 1
    elif [[ $commit == true ]]; then
        git commit "$args"
    elif [[ $checkout == true ]]; then
        git checkout "$args"
    else
        echo "Undefined behavior"
        return 1
    fi
}

However, this does not work properly. After a bit of experimenting, I found that the assigning $@ to another variable was the root cause but I was unable to understand why and what exactly was going wrong.

Also, as I'm writing shell functions for the first time, highlight any mistakes I've made.

Best Answer

$@ is an array, assign it to an array:

args=("$@")

Then use it as an array:

elif [[ $commit == true ]]; then
    git commit "${args[@]}"
elif [[ $checkout == true ]]; then
    git checkout "${args[@]}"
else

What's happening in your current code is that all of your separate arguments are being stored as a single string. So if you call:

bc -a "foo bar"

That gets assigned to args as:

args='-a foo bar'

Then instead of executing:

git commit -a "foo bar"

You get:

git commit '-a foo bar'
Related Question