barczag
barczag

Reputation: 751

Bash env var existence check refactor

I need to check if a list of env vars are existing. Now I'm doing this which of course is a very dumb solution:

if [ -z "$MONGOLAB" ]; then
  echo "Missing \$MONGOLAB"
  exit 1
fi

if [ -z "$APP_PORT" ]; then
  echo "Missing \$APP_PORT"
  exit 1
fi

if [ -z "$ENV" ]; then
  echo "Missing \$ENV"
  exit 1
fi
... more checks here

How could I refactor this? :)

Upvotes: 1

Views: 124

Answers (3)

Toby Speight
Toby Speight

Reputation: 30781

Here's one way to do it:

#!/bin/bash
set -e

err=
for v in MONGOLAB APP_PORT ENV; do
    err="$err${!v-$v not set$'\n'}"
done
if test -n "$err"
then printf "%s" "$err" >&2; exit 1
fi

echo "All environment variables set"
exit 0

It improves on your solution in that it informs the user of all the required but unset variables at once, avoiding the typical annoying back-and-forth with the script to satisfy it.

We build up the error value by adding a message to it for each unset variable, then if it's non-empty, print it and abort.

The ${!v-...} construct is an indirection - if there's a variable named $v, then substitute nothing, else substitute our message.

Alternative version

This uses an array of the required variables, and builds a one-line error message:

#!/bin/bash
set -e

required_vars=(MONGOLAB APP_PORT ENV)
missing_vars=''
for v in "${required_vars[@]}"; do
    missing_vars="$missing_vars${!v-$v }"
done
if test -n "$missing_vars"
then printf "Unset required variables: %s\n" "$missing_vars" >&2; exit 1
fi

echo "All environment variables set"
exit 0

Alternative, as a function

#!/bin/bash
set -e

check_vars() {
    local m=''
    for v in "$@"; do
        m="$m${!v-$v }"
    done
    if test -n "$m"
    then printf "Unset required variables: %s\n" "$m" >&2; return 1
    fi
}

check_vars MONGOLAB APP_PORT ENV

echo "All environment variables set"
exit 0

Upvotes: 1

Rany Albeg Wein
Rany Albeg Wein

Reputation: 3474

Another option is:

[[ $MONGOLAB && $APP_PORT && $ENV ]] || exit 1

If you happen to run a long sanity check with multiple variables, and the code becomes ugly, I suggest to use @anubhava's solution, which is far more elegant.

I usually use a die function instead of exit 1 to be more descriptive to the user in terms of human readable description and exit status code:

##
# die (optional status version): Print a message to
# stderr and exit with either the given status or
# that of the most recent command.
# Usage: some_command || die [status code] "message" ["arguments"...]
#
die() {
  local st="$?"
  if [[ "$1" != *[^0-9]* ]]; then
    st="$1"
    shift
  fi
  warn "$@"
  exit "$st"
}

This function is taken from Common utility functions (warn, die) which I encourage you to read further, and maybe use a different version which suites your needs best. Notice that die uses a function named warn which you can find in the same link above.

P.S.

Please note that by convention, environment variables (PATH, EDITOR, SHELL, ...) and internal shell variables (BASH_VERSION, RANDOM, ...) are fully capitalized. All other variable names should be lowercase. Since variable names are case-sensitive, this convention avoids accidentally overriding environmental and internal variables.

Upvotes: 1

anubhava
anubhava

Reputation: 785068

You can use a for loop and check all of them there:

for var in MONGOLAB APP_PORT ENV; do
    [[ -z "${!var}" ]] && echo "Missing \$$var"
done

Upvotes: 4

Related Questions