4

As explained in this answer, the "right" way to check if a variable is set in bash looks like this:

if [ -z ${var+x} ]; then
    echo "var is unset"
else
    echo "var is set to '$var'"
fi

What I'm interested in is how to extract this into a function that can be reused for different variables.

The best I've been able to do so far is:

is_set() {
  local test_start='[ ! -z ${'
  local test_end='+x} ]'
  local tester=$test_start$1$test_end

  eval $tester
}

It seems to work, but is there a better way that doesn't resort to calling eval?

codeforester
  • 39,467
  • 16
  • 112
  • 140
ivan
  • 6,032
  • 9
  • 42
  • 65
  • Making a function to test if a variable is set doesn't seem like a good idea. You will either be hardcoding the variable you want to check (which makes no sense), using `eval` not recommended, especially when you cannot be absolutely assured of the variable content, or passing the value of the variable to check as a parameter which takes you back to a `[[ -z $1 ]]` test to begin with. – David C. Rankin May 27 '17 at 20:09
  • 5
    There is a subtle distinction here: `[[ -z $var ]]` returns true if `var` is __either__ an empty string __or__ unset. `[[ -z ${var+x} ]]` tests strictly for unset. – John1024 May 27 '17 at 20:11
  • 1
    @John1024: good catch! – Cyrus May 27 '17 at 20:19
  • @DavidC.Rankin I wanted to write a function to make the code self-documenting, since the syntax is pretty cryptic. Given the complications though, I agree with you, and it got me thinking about the difference between documenting the domain logic you're composing and documenting the workings of the language itself. Since the language is resisting my efforts here, I'll stick with what's simplest, and not try to wrap it in a function. – ivan May 28 '17 at 04:22
  • I'm not 100% certain you will get the same results in bash 3, but you can indirectly check a variable using the `!` operator `isunset() { [[ -z ${!1+x} ]];}` You can then use `if isunset var; then echo var is not set` – Dave May 28 '17 at 15:21

3 Answers3

6

In Bash you can use [[ -v var ]]. There's no need for a function or convoluted schemes.

From the manpage:

   -v varname
          True if the shell variable varname is set (has been assigned a value).

The first 2 command sequences prints ok:

[[ -v PATH ]] && echo ok

var="" ; [[ -v var ]] && echo ok

unset var ; [[ -v var ]] && echo ok
codeforester
  • 39,467
  • 16
  • 112
  • 140
Ricardo Branco
  • 5,740
  • 1
  • 21
  • 31
  • I'm guessing this was added in a newer version of Bash than what I'm running? I get `bash: conditional binary operator expected`, but I'm using the version of Bash that came with OSX (3.2.57). I should probably upgrade anyway, but I'm writing this script to help coworkers set up part of their dev environment, so portability is a concern. – ivan May 28 '17 at 04:08
  • 1
    Then a cleaner way like `[[ -z ${var+x} ]]`, as was pointed out earlier, it's the way to go. Does it work in Bash 3? Running `eval` in a function for a simple check is ugly and asking for trouble. – Ricardo Branco May 28 '17 at 04:13
  • 1
    Yes, the `[[ -z ${var+x} ]]` approach works in Bash 3. I'll stick with that. It's good to know that `-v` was added though, thanks! – ivan May 28 '17 at 04:24
2

With [[ ... ]], you can simply do this (there is no need for double quotes):

[[ $var ]] && echo "var is set"
[[ $var ]] || echo "var is not set or it holds an empty string"

If you really want a function, then we can write one-liners:

is_empty()     { ! [[ $1 ]]; } # check if not set or set to empty string
is_not_empty() {   [[ $1 ]]; } # check if set to a non-empty string

var="apple"; is_not_empty "$var" && echo "var is not empty" # shows "var is not empty"
var=""     ; is_empty "$var"     && echo "var is empty"     # shows "var is empty"
unset var  ; is_empty "$var"     && echo "var is empty"     # shows "var is empty"
var="apple"; is_empty "$var"     || echo "var is not empty" # shows "var is not empty"

Finally, is_unset and is_set could be implemented to treat $1 as the name of the variable:

is_unset() {   [[ -z "${!1+x}" ]]; }           # use indirection to inspect variable passed through $1 is unset
is_set()   { ! [[ -z "${!1+x}" ]]; }           # check if set, even to an empty string

unset var; is_unset var && echo "var is unset" # shows "var is unset"
var=""   ; is_set var   && echo "var is set"   # shows "var is set"
var="OK" ; is_set var   && echo "var is set"   # shows "var is set"

Related

codeforester
  • 39,467
  • 16
  • 112
  • 140
0

Bash 4.2+

if [[ -v $MY_VARIABLE ]]; then
    echo "MY_VARIABLE is set"
fi

Older Versions(MacOS)

if [[ -n ${MY_VARIABLE+set} ]]; then
    echo "MY_VARIABLE is set"
else
QuesterDesura
  • 385
  • 2
  • 18