1

I have read the getopts man page and am still not sure about this use case.

getopts is not detecting any available options the second time a function is called in the same script.

As you can see from my debug echo outputs, all of the positional params $@ are present for both function calls.

In the second create_db function call, the getopts while loop is never entered, causing my variables TYPE and ENVIRON to not be set.

Any thoughts?

FUNCTION DEFINITION (create_db)

function create_db {
  local TYPE SIZE ENVIRON
  TYPE=''
  SIZE=''
  ENVIRON=''

  print_usage() {
    echo -e $"\nUsage: create_db -t {mysql|redis|rabbitmq|sftp|elasticsearch} -e <environment_name> -s <size_in_GB>"
    echo "Required args: -t, -e"
    echo "Optional args: -s"
  }

  echo "@: $@"
  echo "0: $0"
  echo "1: $1"
  echo "2: $2"
  echo "3: $3"
  echo "4: $4"
  echo "5: $5"
  echo "6: $6"

  # parse flags
  while getopts 't:s:e:h' flag; do
    echo "flag: $flag"
    echo "opt: ${OPTARG}"
    case "${flag}" in
      t) TYPE="${OPTARG}" ;;
      s) SIZE="${OPTARG}" ;;
      e) ENVIRON="${OPTARG}" ;;
      h) print_usage
         exit 0 ;;
      *) print_usage >&2
         exit 1 ;;
    esac
  done
  shift "$(( OPTIND - 1 ))"

  echo "TYPE: ${TYPE}"
  echo "ENVIRON: ${ENVIRON}"

  ... DO WORK ...

}

CALLED SCRIPT (environment-setup-from-scratch.sh)

#!/bin/bash

# import functions from utils file
. "${0%/*}/environment-setup-utils.sh"

ENVIRONMENT="${1}"

create_db -t "elasticsearch" -e "${ENVIRONMENT}"
create_db -t "mysql" -e "${ENVIRONMENT}"
create_db -t "redis" -e "${ENVIRONMENT}"

TERMINAL OUTPUT

$ ./environment-setup-from-scratch.sh  sandbox


@: -t elasticsearch -e sandbox
0: ./environment-setup-from-scratch.sh
1: -t
2: elasticsearch
3: -e
4: sandbox
5:
6:
flag: t
opt: elasticsearch
flag: e
opt: sandbox
TYPE: elasticsearch
ENVIRON: sandbox


@: -t mysql -e sandbox
0: ./environment-setup-from-scratch.sh
1: -t
2: mysql
3: -e
4: sandbox
5:
6:
TYPE:
ENVIRON:
  • This is a dupe of [this question](https://unix.stackexchange.com/questions/233728/bash-function-with-getopts-only-works-the-first-time-its-run), but unfortunately that doesn't have my preferred solution: using `local OPTIND` in each function that uses `getopts`, so each can keep its own count. See [this stackoverflow answer](https://stackoverflow.com/questions/16654607/using-getopts-inside-a-bash-function/16655341#16655341). – Gordon Davisson Jun 15 '19 at 05:49
  • sorry about the dupe! – Ryan Mahaffey Jun 17 '19 at 18:00

1 Answers1

3

Each time you call getopts, it uses $OPTIND;

If the application sets OPTIND to the value 1, a new set of parameters can be used: either the current positional parameters or new arg values. Any other attempt to invoke getopts multiple times in a single shell execution environment with parameters (positional parameters or arg operands) that are not the same in all invocations, or with an OPTIND value modified to be a value other than 1, produces unspecified results.

(my emphasis). You need to reset OPTIND before you call getopts each time, perhaps here:

# ...
  # parse flags
  OPTIND=1
  while getopts 't:s:e:h' flag; do
# ...
Jeff Schaller
  • 66,199
  • 35
  • 114
  • 250
  • Thank you so much, perfect explanation. Initially thought my `shift "$(( OPTIND - 1 ))" ` would do the trick but I am guessing the scope does not work due to the subshell – Ryan Mahaffey Jun 15 '19 at 01:19