Learn Docker With My Newest Course

Dive into Docker takes you from "What is Docker?" to confidently applying Docker to your own projects. It's packed with best practices and examples. Start Learning Docker →

Call a Dynamically Created Function Name in a Shell Script

call-a-dynamically-created-function-name-in-a-shell-script.jpg

One use case could be to refactor a bunch of if conditions in 1 function into separate isolation functions.

Quick Jump:

Prefer video? Here it is on YouTube.

I recently had a use case in a command line tool where I wanted to do something specific depending on what type of input a user provided.

I wanted to add a few environment variables into a file but the variables were different depending on what input the user provided. For example, imagine if you had 8 different apps each with their own environment variables.

We can do this in a POSIX shell compliant way.

Without dynamic function calls you might choose to do this:

#!/usr/bin/env sh

set -o errexit
set -o nounset

APP="${1}"

create_env() {
  contents=

  if [ "${APP}" = "foo" ]; then
    contents=$(cat << EOF
FOO=something
ANOTHER_VAR=cool
EOF
)
  elif [ "${APP}" = "bar" ]; then
    contents=$(cat << EOF
BAR=something_else
NICE=thanks
EOF
)
  elif [ "${APP}" = "baz" ]; then
    contents=$(cat << EOF
BAZ=yep
HELLO=world
EOF
)
  fi

  [ -n "${contents}" ] && echo "${contents}" > .env
}

create_env

Then if you ran it, you’d get this output:

$ ./demo foo

$ cat .env
FOO=something
ANOTHER_VAR=cool

There are of course many ways to solve this specific problem. For example, you could create different env files on disk and cat out the specific app’s .env file with less code.

I simplified this example a little for the sake of this post. In the real use case, some of these values were being dynamically created based on other variables in the script so I wanted to handle defining the env vars in the script itself.

With that said, here’s how you can refactor the above script to split out the logic of each condition into its own function and then dynamically call a specific function depending on which app name you pass in:

#!/usr/bin/env sh

set -o errexit
set -o nounset

APP="${1}"

create_foo_env() {
  cat << EOF > .env
FOO=something
ANOTHER_VAR=cool
EOF
}

create_bar_env() {
  cat << EOF > .env
BAR=something_else
NICE=thanks
EOF
}

create_baz_env() {
  cat << EOF > .env
BAZ=yep
HELLO=world
EOF
}

"create_${APP}_env"

Using the script is the same as the first version we covered. We don’t even need to protect against an empty .env file from being generated because set -o nounset will throw an error if we input an app name that doesn’t have a function associated with it.

The takeaway is the last line. It’s really that easy. You can reference the variable as part of the function name. I wrapped it in quotes to make shellcheck happy but it technically works without quotes too.

I find the above mentally easier to reason about because the contents of the file isn’t mixed in with the conditions to figure out which content to use. This example only has 3 apps with 2 env vars too. It was much worse with 8 apps which had more env vars.

I used a similar pattern when creating dynamic variables in a previous post. Sometimes it’s nice to apply the same concept to solve multiple use cases. Also, if you’re curious I covered using heredocs (the << EOF syntax) in detail in the past.

The video below covers both versions of the code.

# Demo Video

Timestamps

  • 0:30 – Without dynamic function calls
  • 2:24 – Refactoring the script to use a dynamic function call
  • 4:48 – IMO this makes the code easier to read

When was the last time you dynamically called a function? Let us know!

Never Miss a Tip, Trick or Tutorial

Like you, I'm super protective of my inbox, so don't worry about getting spammed. You can expect a few emails per year (at most), and you can 1-click unsubscribe at any time. See what else you'll get too.



Comments