Adding Short (and Long) Flags to Bash Scripts

Adding flags to your bash script will probably simplify your developing experience. This is a short guide on how you can add both long and short flags without using getopts.


Adding Flags

Adding flags can be done in many different ways, the most common way is probably using getopts. However, in that case your a limited to using only short flags (-f instead of --flag, for example.). These are some simple ways on how you can do it with a simple loop instead, which allows long arguments as well as short.

We will use a simple script that takest two arguments, --tag and --skip-verification. Those are a bit different, as tag takes a value, while --skip-verification is just a boolean. The command line using should look something like this.

Look at the bottom for a tl;dr.

./flags.sh -t latest --skip-verification

Example 1 - While Loop

# if no arguments are provided, return usage function
if [ $# -eq 0 ]; then
    usage # run usage function
    exit 1
fi

SKIP_VERIFICATION=false
TAG=

while [ "$1" != "" ]; do
    case $1 in
    --skip-verification)
        SKIP_VERIFICATION=true
        ;;
    -t | --tag)
        shift # remove `-t` or `--tag` from `$1`
        TAG=$1
        ;;
    -h | --help)
        usage # run usage function
        ;;
    *)
        usage
        exit 1
        ;;
    esac
    shift # remove the current value for `$1` and use the next
done

Explanation While Loop

We are looping through $1, which is the first passed argument. In the example code above, it will be -t.

As $1 matches -t in the case scenario, that case is used.

shift
TAG=$1
;;

The shift command removes the current command from the arguments array, meaning $1 will no longer be -t, but the next argument in the array, which in this case is latest. The argument latest will then be added to the TAG variable.

The shift command is necessary as we want the value of the tag. There is a differece with the --skip-verification flag, as no value is provided. In that case, we do not need a shift statement.

SKIP_VERIFICATION=true
;;

We only set SKIP_VERIFICATION to true.

As a final step, we use the shift statement once again after the case statement. This is to further our while loop, making $1 the next argument that has been provided. If the argument is empty, the loop is done.

Example 2 - For Loop

Instead of using a while loop, we can use a for loop to loop through all arguments in the arguments array. This example does the exact same thing as the previous one.

# if no arguments are provided, return usage function
if [ $# -eq 0 ]; then
    usage # run usage function
    exit 1
fi

SKIP_VERIFICATION=false
TAG=

for arg in "$@"; do
    case $arg in
    --skip-verification)
        SKIP_VERIFICATION=true
        shift # Remove --skip-verification from `$@`
        ;;
    -t | --tag)
        TAG=$2
        shift # Remove argument (-t) name from `$@`
        shift # Remove argument value (latest) from `$@`
        ;;
    -h | --help)
        usage # run usage function on help
        ;;
    *)
        usage # run usage function if wrong argument provided
        ;;
    esac
done

Explanation For Loop

This works the same way as above. There is not much difference in the logic. We iterature through all arguments and use the shift statement to remove the current one. The big difference is the loop as well as when we use the shift. In the end, it all comes down to preference.

Logic with Flags

The flags have been saved to variables, which you can use in your code later on. These are some examples.

if [[ "$TAG" == "" ]]; then
    echo "You must provide a tag";
    exit 1;
fi

if [[ $SKIP_VERIFICATION == false ]]; then
    # do not verify
else
    # do verify
fi

TL;DR

This is the full code:

#! /bin/bash

function usage() {
    cat <<USAGE

    Usage: $0 [-t tag] [--skip-verification]

    Options:
        -t, --tag:            information about argument
        --skip-verification:  information about argument
USAGE
    exit 1
}

if [ $# -eq 0 ]; then
    usage
    exit 1
fi

SKIP_VERIFICATION=false
TAG=

while [ "$1" != "" ]; do
    case $1 in
    --skip-verification)
        SKIP_VERIFICATION=true
        ;;
    -t | --tag)
        shift
        TAG=$1
        ;;
    -h | --help)
        usage
        ;;
    *)
        usage
        exit 1
        ;;
    esac
    shift
done

if [[ $TAG == "" ]]; then
    echo "You must provide a tag";
    exit 1;
fi

if [[ $SKIP_VERIFICATION == false ]]; then
    # do not verify
else
    # do verify
fi