Passing a boolean flag to a function?

You may supply a command line option to your function. Using command line options that takes no argumets is a common way of providing binary/boolean values ("on/off", "true/false", "enable/disable") to shell scripts, shell functions and utilities in general.

foo () {
    local flag=false
    OPTIND=1

    while getopts 't' opt; do
        case $opt in
            t) flag=true ;;
            *) echo 'Error in command line parsing' >&2
               exit 1
        esac
    done
    shift "$(( OPTIND - 1 ))"

    local param1="$1"
    local param2="$2"

    if "$flag"; then
        # do things for "foo -t blah blah"
    else
        # do things for "foo blah blah"
    fi
}

The option -t acts like a boolean flag for the user. Using it would set flag inside the function to true (changing it from its default value of false). The -t option would be used as the first argument to the function.

Calling the function would be done using

foo "some value" "some other value"

or

foo -t "some value" "some other value"

where the latter call would set the flag variable in the function to true.


In general

In general passing booleans in to functions, in any language, is unreadable. e.g. calculate_interest 5y 4% true. The reader is left wondering what true is.

Therefore use an enumeration: { per_month, per_year }. Now you can do calculate_interest 5y 4% per_year. This is more readable.

In bash

Bash is not statically typed, (or strongly typed, or have much of a type system at all), so you can pass in many different values. One will lead this way, the others will not. It is not desired to have many different values that lead down the same path.

Therefore add code to check that the input is one of the two acceptable values. Do this at the start of the function, you don't want to bail out ½ way through doing something.

What to do if you have to call a function that takes a Boolean

So some one did not follow my advice, and you have to call a function that takes a boolean. What can you do to make your code readable?

  • If the language (such as Python) allows named arguments e.g. calculate_interest 5y 4% per_year=True, then use them. It will help, but does not tell you the meaning of calculate_interest 5y 4% per_year=False.
  • If the language does not have named arguments, then the only options are to live with unreadable code (not an option), or to wrap the functions in literate functions.

I recommend to follow a format like this:

foo() {
  # Limit scope of variables
  local 'opt1' 'opt2' 'opt3' 'operands'

  # Default values
  opt1='default1'
  opt2='default2'
  opt3='false'
  operands=()

  # Arguments handling
  while (( ${#} > 0 )); do
    case "${1}" in
      ( '--opt1='* ) opt1="${1#*=}" ;;           # Handles --opt1
      ( '--opt2='* ) opt2="${1#*=}" ;;           # Handles --opt2
      ( '--opt3='* ) opt3="${1#*=}" ;;           # Handles --opt3
      ( '--' ) operands+=( "${@:2}" ); break ;;  # End of options
      ( '-'?* ) ;;                               # Discard non-valid options
      ( * ) operands+=( "${1}" )                 # Handles operands
    esac
    shift
  done

  ...
}

That way the function will be more robust and readable:

$ foo
Options:
  opt1: [default1]
  opt2: [default2]
  opt3: [false]
$ foo --opt1='value1' --opt2='value2' --opt3='true' 'foo' 'bar' 'baz'
Options:
  opt1: [value1]
  opt2: [value2]
  opt3: [true]
Operands:
  1: [foo]
  2: [bar]
  3: [baz]

Pros:

  • Easy to read and understand.
  • Syntax is similar to any typical command-line utility.
  • It's easy to add more options without breaking compatibility.

Cons:

  • May be overkill for small and simple scripts.
  • Hard to write a portable and POSIX-compliant equivalent that also handles long-options and operands.