d
Amit DhamuSoftware Engineer
 

Flags in Bash with getopts

4 minute read 00000 views

When writing a Bash script, sometimes you may want to pass arguments (or flags) to the script to drive specific behaviour. We can utilise getopts to do this.

Let's create a simple script that will output our name. We will need to provide it forename and surname. See the usage function which describes how we want our program to be executed.

#!/usr/bin/env bash

set -e

function usage() {
  echo '
    This program outputs your name

    -f [forename] - your first name
    -s [surname] - your last name

    example: ./'$(basename "$0")' -f [forename] -s [surname]
    example: ./'$(basename "$0")' -f Amit -s Dhamu
'
}

Next, we need to add something in to handle the -f and -s flags.

while getopts 'f:s:' flag; do
  case "${flag}" in
    f) FORENAME="${OPTARG}" ;;
    s) SURNAME="${OPTARG}" ;;
    *)
      usage
      exit 1
      ;;
  esac
done

getopts is declared here with a while loop and f:s:.

To explain the colons:

An option character in this string can be followed by a colon (‘:’) to indicate that it takes a required argument. If an option character is followed by two colons (‘::’), its argument is optional; this is a GNU extension.

https://www.gnu.org/software/libc/manual/html_node/Using-Getopt.html

Then for the case statement:

  • f) - we store the contents of the supplied flag to the FORENAME variable
  • s) - we store the contents of the supplied flag to the SURNAME variable
  • *) - handles any other flags which are not supported. If those are supplied, we issue the usage message and exit with a non-zero exit code

Let's finish off our script by checking FORENAME and SURNAME are not empty. If they are, we issue the usage message and exit with a non-zero exit code. If not, we print out a welcome message with the supplied name.

if [[ -z "${FORENAME}" || -z "${SURNAME}" ]]; then
  echo "WARNING: You must supply your forename and surname"
  usage
  exit 1
fi

echo "Welcome ${FORENAME} ${SURNAME}"

Optional Arguments

Let's say we wanted to add in a title but didn't want to make it required. We can change our getopts and case statement like below.

- while getopts 'f:s:' flag; do
+ while getopts 'f:s:t::' flag; do
   case "${flag}" in
     f) FORENAME="${OPTARG}" ;;
     s) SURNAME="${OPTARG}" ;;
+    t) TITLE="${OPTARG} " ;;
     *)
       usage
       exit 1
       ;;
   esac
 done

Given we have a separate if statement to validate the required arguments, we can simply change our echo statement.

echo "Welcome ${TITLE}${FORENAME} ${SURNAME}"