1

I have been creating a small library of bash functions to encapsulate some of the more arcane bash syntax structures into routines that I can quickly use and reference. But for some of them, I'm running into unexpected return codes from my functions. The 'is_undefined' function below is one such example. Can anyone explain the results I am getting? (Also provided below.)

#!/bin/bash

is_undefined ()
{
  # aka "unset" (not to be confused with "set to nothing")
  # http://stackoverflow.com/questions/874389/bash-test-for-a-variable-unset-using-a-function
  [ -z ${1+x} ]
}

if [ -z ${UNDEFINED+x} ]; then
  echo "inline method reports that \$UNDEFINED is undefined"
fi

if is_undefined UNDEFINED; then
  echo "is_undefined() reports that \$UNDEFINED is undefined"
else
  echo "is_undefined() reports that \$UNDEFINED is defined"
fi

DEFINED=
if is_undefined DEFINED; then
  echo "is_undefined() reports that \$DEFINED is undefined"
else
  echo "is_undefined() reports that \$DEFINED is defined"
fi

The surprising results are:

$ ./test.sh
inline method reports that $UNDEFINED is undefined
is_undefined() reports that $UNDEFINED is defined
is_undefined() reports that $DEFINED is defined
Cognitive Hazard
  • 1,072
  • 10
  • 25
  • 1
    add `set -vx` to see how/what is being processed when. Good luck. – shellter Dec 06 '13 at 23:05
  • The newest version of `bash` has a `-v` operator to test if a variable is set. `[[ -v foo ]]` succeeds only if `foo` is not set; if `foo` is set to the empty string it fails. – chepner Dec 06 '13 at 23:16
  • Regarding -v: I managed to get that into my code several months back, but forgot to document a reference. I removed it last night because I failed to find it mentioned in my bash book, and googling it yielded no results. I thought I must have imagined it... I guess not! :) – Cognitive Hazard Dec 06 '13 at 23:19
  • 1
    Oh, and my description of `-v`is completely backwards :( `-v foo` is true when `foo` *is* set, and false otherwise. Introduced in 4.2. – chepner Dec 06 '13 at 23:45

2 Answers2

3

is_undefined UDEFINED returns true, because the test inside is_undefined doesn't test UNDEFINED but $1, and $1 is defined. It's value is UNDEFINED.

So, your function should always return true, as long as you provide a parameter. The only time it will return false, should be when you call it with no arguments

is_undefined

To get is_undefined test the actual variable, you can use variable indirection with an exclamation mark !, see Shell Parameter Expansion

is_undefined ()
{
  # aka "unset" (not to be confused with "set to nothing")
  # http://stackoverflow.com/questions/874389/bash-test-for-a-variable-unset-using-a-function
  [ -z "${!1+x}" ]
}
chepner
  • 497,756
  • 71
  • 530
  • 681
Olaf Dietsche
  • 72,253
  • 8
  • 102
  • 198
  • That makes sense. But what must I do to dereference $1 within that expression inside `is_undefined()`, to get the behavior I desire? – Cognitive Hazard Dec 06 '13 at 23:04
  • You can use variable indirection, see updated answer. – Olaf Dietsche Dec 06 '13 at 23:10
  • Thanks! (Meta: I am uncertain whether to accept your answer, or @yaccz's answer. He provided the variable indirection component slightly before you did, but both of your answers were equally helpful. What's etiquette, in this scenario?) – Cognitive Hazard Dec 06 '13 at 23:15
  • I don't know about any etiquette, in the end it's up to you. In either case, I won't mind, if you accept his answer. – Olaf Dietsche Dec 06 '13 at 23:19
3

inside is_undefined you are testing $1, not ${UNDEFINED}, to do that you need throw in variable indirection like

is_undefined () {
    [ -z "${!1+x}" ]
}

However, that is bashism and not posix compliant. For posix compliacy you will need

is_undefined () {
    eval "[ -z \${$1+x} ]"
}
Jan Matějka
  • 1,880
  • 1
  • 14
  • 31