qwerty
qwerty

Reputation: 3869

Bash function to check for unset and blank variables

I'm writing a bash function to check for blank or unset variables.

function exit_if_var_does_not_exist {
    TMP_NAME=$1
    TMP_VALUE=`echo $TMP_NAME`
    if [ -z ${TMP_VALUE+x} ]; then
        echo "Variable [${TMP_NAME}] is unset. Exiting."
        exit 1
    else
        if [ ${TMP_VALUE} = "" ]; then
            echo "Variable [${TMP_NAME}] is set to ''. Exiting."
            exit 1
        fi
    fi
    echo "Variable [${TMP_NAME}] is set to ${TMP_VALUE}"
}

VAR=Test
exit_if_var_does_not_exist VAR
BLANK=
exit_if_var_does_not_exist BLANK

This does not give me the expected output in TMP_VALUE. Can someone please help me with what I'm missing here?

-bash-3.2$ ./x.sh
Variable [VAR] is set to VAR
Variable [BLANK] is set to BLANK

Upvotes: 4

Views: 387

Answers (3)

cuonglm
cuonglm

Reputation: 2806

Another way:

exit_if_var_does_not_exist() {
  if ! (set -u; : "${!1}") >/dev/null 2>&1; then
    printf 'Variable [%s] is unset. Exiting.\n' "$1"
    exit 1
  elif [ -z "${!1}" ]; then
    printf "Variable [%s] is set to ''. Exiting.\n" "$1"
    exit 1
  fi
  printf 'Variable [%s] is set to %s\n' "$1" "${!1}"
}

If you don't need to separate the two error messages:

exit_if_var_does_not_exist() {
  : "${!1:?$1 is unset or blank}"
  printf 'Variable [%s] is set to %s\n' "$1" "${!1}"
}

Upvotes: 1

Etan Reisner
Etan Reisner

Reputation: 80921

The problem with the empty test block is that at no point in this snippet do you ever actually get the value of the originally named variable.

When you use TMP_NAME=$1 you assign the name of the input variable to TMP_NAME and then

TMP_VALUE=`echo $TMP_NAME`

just assigns that name to TMP_VALUE. Effectively you just ran TMP_VALUE=$1.

So you aren't testing for whether the originally named variable has contents anywhere here.

To do that you need to use indirect expansion.

Namely TMP_VALUE=${!TMP_NAME} or TMP_VALUE=${!1}.

Side comment your "unset" test block at the top can never trigger.

TMP_VALUE can never be unset because you assign to it. Even TMP_VALUE= marks the variable as "set". So that bit is useless. Though, and thank David C. Rankin for trying long enough to make me think of this, you can use an indirect expansion trick to make this work.

[ -z "${!TMP_NAME+x}" ] will return true for any set variable named in TMP_NAME and false for an unset variable.

That all said if what you want to do is error if a variable is unset or blank the shell has you covered. Just use

: "${VAR:?Error VAR is unset or blank.}" || exit 1

Finally, as David C. Rankin points out inside [ you need to quote expansions that might disappear when they change the meaning of tests (you see this in the -z test above) as well as here.

So [ ${TMP_VALUE} = "" ] needs to be [ "${TMP_VALUE}" = "" ] because is TMP_VALUE is empty the first version would become a syntax error because [ = "" ] isn't a valid invocation of test/[.

Upvotes: 3

David C. Rankin
David C. Rankin

Reputation: 84521

I'm not sure this will work for all cases, but try:

#!/bin/bash

function exit_if_var_does_not_exist {
    local TMP_NAME=$1
    local TMP_VALUE="${!TMP_NAME}"
    [ -z "${!TMP_NAME}" ] && {
        echo "Variable [${TMP_NAME}] is unset. Exiting."
        return 1
    }
    [ "$TMP_VALUE" = "" ] && {
        echo "Variable [${TMP_NAME}] is set to ''. Exiting."
        return 1
    }
    echo "Variable [${TMP_NAME}] is set to ${TMP_VALUE}"
}

VAR=Test
exit_if_var_does_not_exist VAR
# BLANK=
exit_if_var_does_not_exist BLANK

Output

$ bash tstempty.sh
Variable [VAR] is set to Test
Variable [BLANK] is unset. Exiting.

Improved Indirection Soup

After a bit of discussion in the comments and a suggestion, I think I have a version that works more consistently. Again, I qualify this with I am not sure it will work in all situations, but for the testing it seems to:

#!/bin/bash

function exit_if_var_does_not_exist {
    local TMP_NAME=$1
    local TMP_VALUE="${!TMP_NAME}"
    if ! declare -p $1 >/dev/null 2>&1 ; then
        [ -z "${!TMP_NAME}" ] && {
            echo "Variable [${TMP_NAME}] is unset. Exiting."
            return 1
        }
    elif [ "$TMP_VALUE" = "" ]; then
        echo "Variable [${TMP_NAME}] is set to ''. Exiting."
        return 1
    fi
    echo "Variable [${TMP_NAME}] is set to ${TMP_VALUE}"    
}

VAR=Test
exit_if_var_does_not_exist VAR
# BLANK=
exit_if_var_does_not_exist BLANK
EMPTY=""
exit_if_var_does_not_exist EMPTY

Output

$ bash tstempty.sh
Variable [VAR] is set to Test
Variable [BLANK] is unset. Exiting.
Variable [EMPTY] is set to ''. Exiting.

Upvotes: 2

Related Questions