1

After a lot of read, SO or others, I'm really wondering about the best / cleanest way to have a bash script with parameters, optionals with default values.

Here is my script for now:

#!/bin/bash

helpFunction()
{
   echo ""
   echo "Usage: $0 --reload --mode=[single|cluster]"
   echo -e  "\t--reload Reload the database : fixtures & schema"
   echo -e  "\t--mode Mode of build : single or cluster"
   exit 1
}

while [[ "$#" -gt 0 ]]; do
    case $1 in
        -h|--help) helpFunction; shift ;;
        -r|--reload) reload=true; shift ;;
        -m|--mode) mode="single"; shift ;;
        # ... (same format for other required arguments)
        *) echo "Unknown parameter passed: $1" ;;
    esac
    shift
done

./bin/sh/tools/build.sh -e local -m $mode -p local

For now, the $mode variable seems to not be set if I don't set it, how can I have a default value for this variable ? (default is single)

What I want is the user to call the script like (reload is true, mode is cluster):

bin/script.sh -r --mode=cluster 

Or by default (reload is false, mode is single):

bin/script.sh

Is this the good way to wait for parameters ? I read other ways, but no real explanations.

Thanks.

Vincent Decaux
  • 9,857
  • 6
  • 56
  • 84

2 Answers2

2

If you set the mode beforehand, that will be its default. If you want parameter with assignment, you need to grab $2 and shift twice.

mode="single"
while [[ "$#" -gt 0 ]]; do
    case $1 in
        -h|--help) helpFunction; shift ;;
        -r|--reload) reload=true; shift ;;
        -m|--mode) mode="$2"; shift; shift ;;
        # ... (same format for other required arguments)
        *) echo "Unknown parameter passed: $1" ;;
    esac
    shift
done

I think it's also good to restore the positional arguments. Since you may want to use them, but that depends on your script.

mode="single"
reload="false"

POSITIONAL=()
while [[ "$#" -gt 0 ]]; do
  case $1 in
  -h | --help)
    helpFunction
    shift
    ;;
  -r | --reload)
    reload=true
    shift
    ;;
  -m | --mode)
    mode="$2"
    shift
    shift
    ;;
  *)
    POSITIONAL+=("$1")
    shift
    ;;
  esac
done
set -- "${POSITIONAL[@]}"

COMMAND="$1"

Then you could invoke a function that is called like the $COMMAND and pass the rest of the params with $@, and shift again and set the subcommand to $1. You can do this infinitely to get a chain of (sub)commands.

You can still check if a valid command was provided before you try to call the $COMMAND.

COMMAND_LIST="add issue revoke"

case $COMMAND_LIST in
*"$COMMAND"*)
  "$COMMAND" "$@"
  ;;
*)
  helpFunction
  echo "ERROR: Unknown command: $COMMAND"
  exit 1
  ;;
esac

But that is of course only if your script supports commands/subcommands. Otherwise, it makes sense to error right away like you did, if there is an unknown parameter.

The Fool
  • 16,715
  • 5
  • 52
  • 86
2

There is no universal best/cleanest method; it eventually comes down to what works best for you; some ideas:

  1. set mode to a default value before the while/case loop
  2. after the while/case loop test mode and if unset/undefined then set to a default value (should probably play it safe and unset mode before the while/case loop though in this case you might as well see idea #1)
  3. pass to build.sh wrapped in double quotes (ie, build.sh -e local -m "$mode" -p local) and have build.sh test for the -m argument being unset/undefined and set to a default value
  4. a variation on #2 and #3 is to use parameter substitution when passing mode to build.sh, eg: build.sh -e local -m "${mode:-default_value}" -p local
  5. if your script sources a config/ini file you could assign mode a default value in said config/ini file and then make sure said config/ini file is sourced before the while/case loop
markp-fuso
  • 28,790
  • 4
  • 16
  • 36