129

I'm currently writing a bash testing framework, where in a test function, both standard bash tests ([[) as well as predefined matchers can be used. Matchers are wrappers to '[[' and besides returning a return code, set some meaningful message saying what was expected.

Example:

string_equals() {
    if [[ ! $1 = $2 ]]; then
            error_message="Expected '$1' to be '$2'."

            return 1
    fi
}

So, when a matcher is used, and it fails, only then an error_message is set.

Now, at some point later, I test whether the tests succeeded. If it succeeded, I print the expectation in green, if it failed in red.

Furthermore, there may be an error_message set, so I test if a message exists, print it, and then unset it (because the following test may not set an error_message):

if [[ $error_message ]]; then
    printf '%s\n' "$error_message"

    unset -v error_message
fi

Now my question is, if it is better to unset the variable, or to just set it to '', like

error_message=''

Which one is better? Does it actually make a difference? Or maybe should I have an additional flag indicating that the message was set?

2
  • 1
    If you never compare error_message with anything else, I'd say it doesn't matter. However, I think you want [[ $error_message ]], otherwise you are testing that the literal string "error_message" exists. Commented Sep 4, 2012 at 12:44
  • @chepner Yeah, was a typo. Fixed it. Commented Sep 4, 2012 at 14:13

4 Answers 4

162

Mostly you don't see a difference, unless you are using set -u:

/home/user1> var=""
/home/user1> echo $var

/home/user1> set -u
/home/user1> echo $var

/home/user1> unset var
/home/user1> echo $var
-bash: var: unbound variable

So really, it depends on how you are going to test the variable.

I will add that my preferred way of testing if it is set is:

[[ -n $var ]]  # True if the length of $var is non-zero

or

[[ -z $var ]]  # True if zero length
Sign up to request clarification or add additional context in comments.

7 Comments

var= is not "not set". It's just an unquoted empty string. In addition to set -u, bash's various forms of parameter expansion can also distinguish between unset and null values: ${foo:bar} expands to "bar" when foo is unset, but "" when foo is null, while ${foo:-bar} expands to "bar" if foo is unset or null.
[[ -n $var ]] is false if var is set to the empty string.
if you use 'declare -p' to check for variable 'existence' then var= will show 'declare -- var=""' you must use unset to get rid of it, of course if its a readonly variable then you cant get rid of it of course. Furthermore, if you var=something inside a function, you wont have to worry about getting rid of it if you use "local var=value" be careful though, because "declare var=value" is also local.In the case of declare you have to explicitly set it global using "declare -g var=value", without declare you have to explicitly set it to be local using "local".Global vars are only erased /w unset.
@osirisgothra: good point about local variables. Of course generally it is best to declare (or localise) all variables in a function so that it is encapsulated.
@chepner should be ${foo-bar} instead of ${foo:bar}. test for yourself: unset a; echo ">${a:-foo}-${a:foo}-${a-foo}<"
|
19

As has been said, using unset is different with arrays as well

$ foo=(4 5 6)

$ foo[2]=

$ echo ${#foo[*]}
3

$ unset foo[2]

$ echo ${#foo[*]}
2

Comments

3

Based on the comments above, here is a simple test:

isunset() { [[ "${!1}" != 'x' ]] && [[ "${!1-x}" == 'x' ]] && echo 1; }
isset()   { [ -z "$(isunset "$1")" ] && echo 1; }

Example:

$ unset foo; [[ $(isunset foo) ]] && echo "It's unset" || echo "It's set"
It's unset
$ foo=     ; [[ $(isunset foo) ]] && echo "It's unset" || echo "It's set"
It's set
$ foo=bar  ; [[ $(isunset foo) ]] && echo "It's unset" || echo "It's set"
It's set

Here is a suggested improvement:

isset()   { [[ $(eval echo "\${${1}+1}") ]]; }
isunset() { ! isset "$1"; }

Example:

$ unset foo; isunset foo && echo "It's unset" || echo "It's set"
    It's unset
$ foo=     ; isunset foo && echo "It's unset" || echo "It's set"
    It's set
$ foo=bar  ; isunset foo && echo "It's unset" || echo "It's set"
    It's set

1 Comment

isunset() { stmp=$(mktemp -u XXXXXXXXXXXXXX); [[ "${!1-$stmp}" == "$stmp" ]] || return 1; } isset() { if isunset "$1"; then return 1; fi }
2

So, by unset'ting the array index 2, you essentially remove that element in the array and decrement the array size (?).

I made my own test..

foo=(5 6 8)
echo ${#foo[*]}
unset foo
echo ${#foo[*]}

Which results in..

3
0

So just to clarify that unset'ting the entire array will in fact remove it entirely.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.