Search code examples
bashshellunixgetopts

how to effectively test for valid string input using getopts in bash script


I'm using getopts in my bash script to pass in 2 parameters.

I'm trying to enforce a string match (using if statement) on my input for -c <component> to enter the following options:

Note: it has to match the exact string for these 5 options:

customer_builder
customer_service
historic_customer_builder
stream_builder
data_distributor

Code in progress:

# Usage for getopts
usage () {
    echo -e "Usage:$0 -r region -c <component>"
    echo -e "Available components:"
    echo -e "customer_builder|customer_service|historic_customer_builder|stream_builder|data_distributor"
    echo -e "Example:$0 -r us-east-1 -c customer_builder"
}

while getopts ":r:c:" opt; do
  case $opt in
    r) region="$OPTARG";;
    c) component="$OPTARG"
       if [[ "${c}" != "customer_builder" && "${c}" != "customer_service" && "${c}" != "historic_customer_builder" && "${c}" != "stream_builder" && "${c}" != "data_distributor" ]]; then
         usage
         exit 1
       fi
       ;;
    *) usage
       exit 1
       ;;
  esac
done

The main logic I am trying to use and need help with:

   if [[ "${c}" != "customer_builder" && "${c}" != "customer_service" && "${c}" != "historic_customer_builder" && "${c}" != "stream_builder" && "${c}" != "data_distributor" ]]; then
     usage
     exit 1
   fi

So in my testing, I cannot get the string to be enforced.

I expect to get usage if I enter the string incorrectly:

./script.sh -r us-east-1 -c customer-builder
./script.sh -r us-east-1 -c customer
./script.sh -r us-east-1 -c builder
./script.sh -r us-east-1 -c foo_bar

However, I should expect the script to execute if I enter it correctly:

./script.sh -r us-east-1 -c customer_builder
./script.sh -r us-east-1 -c customer_service
./script.sh -r us-east-1 -c stream_builder

So my question is how would you handle and check for proper string input? Is there a better way to write my test?


Solution

  • You are testing the wrong parameter. $c isn't defined, but you just saved -c's argument in $component.


    That said, another another case statement might be simper than your long if condition, and doesn't rely on bash extensions.

    case $component in
      customer_builder|\
      customer_service|\
      historic_customer_builder|\
      stream_builder|\
      data_distributor)
        :  # Do nothing
        ;;
      *)
        usage
        exit 1
        ;;
    esac
    

    OK, now that I've gotten try-to-stay-POSIX-compatible out of my system, here's a simpler if statement using [[...]]'s pattern matching abilities. (This will work as-is in newer versions of bash; older versions will require shopt -s extglob to enable the @(...) syntax.)

    if [[ $component != @(customer_builder|customer_service|historic_customer_builder|stream_builder|data_distributor) ]]; then