Shell: What Is The Purpose of ${Var:-} When Var Is Unset or Null

shell: What is the purpose of ${var:-} when var is unset or null?

The syntax ${debian_chroot:-} prevents the shell from exiting if it is running with set -u (crash when using undefined variables) and debian_chroot is unset at that point.

You don't want a normal interactive shell to have set -u (it would crash too easily), but it can be very useful in scripts.

To see this:

bash -c 'set -u; [ -z $a ]; echo ok'          # error
bash -c 'set -u; a=; [ -z $a ]; echo ok' # ok
bash -c 'set -u; [ -z ${a:-} ]; echo ok' # ok
bash -c 'set -u; a=; [ -z ${a:-} ]; echo ok' # ok

What is the purpose of setting a variable default to empty in bash?

"Null" means the variable has a value, and this value is an empty string. The shell knows the variable exists.

"Unset" means the variable has not been defined : it does not exist as far as the shell is concerned.

In its usual mode, the shell will expand null and unset variable to an empty string. But there is a mode (set -u) that allows the shell to throw a runtime error if a variable is expanded when it is unset. It is good practice to enable this mode, because it is very easy to simply mis-type a variable name and get difficult to debug errors.

It can actually be useful from a computing perspective to differentiate between unset and empty variables, you can assign separate semantics to each case. For instance, say you have a function that may receive an argument. You may want to use a (non-null) default value if the parameter is unset, or any value passed to the function (including an empty string) if the parameter is set. You would do something like :

my_function()
{
echo "${1-DEFAULT_VALUE}"
}

Then, the two commands below would provide different outputs:

my_function     # Echoes DEFAULT_VALUE
my_function "" # Echoes an empty line

There is also a type of expansion that does not differentiate between null and not set :

"${VAR:-DEFAULT_VALUE}"

They are both useful depending on what you need.

The way to test if a variable is set or not (without running the risk of a runtime error) is the following type of expansion :

"${VAR+VALUE}"

This will expand to an empty string if VAR is unset, or to VALUE if it is set (empty or with a value). Very useful when you need it.

Generally, it is helpful to:

  • Declare variables explicitely
  • set -u to prevent silent expansion failure
  • Explicitly handle unset variables through the appropriate expansion

This will make your scripts more reliable, and easier to debug.

Using unset vs. setting a variable to empty

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

difference between unset and empty variables in bash

if [ `set | grep '^VAR=$'` ]

This searches for the string "VAR=" in the list of variables set.

What is the meaning of a question mark in bash variable parameter expansion as in ${var?}?

It works almost the same as (from the bash manpage):

${parameter:?word}
Display Error if Null or Unset. If parameter is null or unset, the expansion of word (or a message to that effect if word is not present) is written to the standard error and the shell, if it is not interactive, exits. Otherwise, the value of parameter is substituted.

That particular variant checks to ensure the variable exists (is both defined and not null). If so, it uses it. If not, it outputs the error message specified by word (or a suitable one if there is no word) and terminates the script.

The actual difference between that and the non-colon version can be found in the bash manpage above the section quoted:

When not performing substring expansion, using the forms documented below, bash tests for a parameter that is unset or null. Omitting the colon results in a test only for a parameter that is unset.

In other words, the section above can be modified to read (basically taking out the "null" bits):

${parameter?word}
Display Error if Unset. If parameter is unset, the expansion of word (or a message to that effect if word is not present) is written to the standard error and the shell, if it is not interactive, exits. Otherwise, the value of parameter is substituted.

The difference is illustrated thus:

pax> unset xyzzy ; export plugh=

pax> echo ${xyzzy:?no}
bash: xyzzy: no

pax> echo ${plugh:?no}
bash: plugh: no

pax> echo ${xyzzy?no}
bash: xyzzy: no

pax> echo ${plugh?no}

pax> _

There, you can see that while both unset and null variable result in an error with :?, only the unset one errors with ?.

Posix shell: distinguish between empty and not existing variable

You can use set

If no options or arguments are specified, set shall write the names and values of all shell variables in the collation sequence of the current locale. Each name shall start on a separate line, using the format:

You can list all the variables (set) and grep for the variable name you want to check

set | grep '^foo='

What is the result of ${VAR:=value} in a bash script

As per documentation:

${parameter:=word}

If parameter is unset or null, the expansion of word is assigned to parameter. The value of parameter is then substituted. Positional parameters and special parameters may not be assigned to in this way.

So it exports the VAR value with its value, or if it is unset/null it is exported with literal value.

Shell script - exiting script if variable is null or empty

There is a built-in operator for requiring that a variable is set. This will cause the script to exit if it isn't.

tag=${1?Need a value}

Commonly this is used with the : no-op near the beginning of the script.

: ${1?Need a value}

The conflation of "unset or empty" is somewhat different. There is no similar construct for exiting on an empty but set value, but you can easily use the related syntax ${var:-default} which expands to $var if it is set and nonempty, and default otherwise. There is also ${var-default} which only produces default if the variable is properly unset.

This can be particularly useful when you want to use set -u but need to cope with a possibly unset variable:

case ${var-} in '') echo "$0: Need a value in var" >&2; exit 1;; esac

I somewhat prefer case over if [ "${var-}" = '' ], mainly because it saves me from having to wrap double quotes around ${var-}, and the pesky case of a value in $var which gets interpreted as an option to [ and gives you an error message when you least expect it. (In Bash, [[ doesn't have these problems; but I prefer to stick to POSIX shell when I can.)

How to check if a variable is set in Bash

(Usually) The right way

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

where ${var+x} is a parameter expansion which evaluates to nothing if var is unset, and substitutes the string x otherwise.

Quotes Digression

Quotes can be omitted (so we can say ${var+x} instead of "${var+x}") because this syntax & usage guarantees this will only expand to something that does not require quotes (since it either expands to x (which contains no word breaks so it needs no quotes), or to nothing (which results in [ -z ], which conveniently evaluates to the same value (true) that [ -z "" ] does as well)).

However, while quotes can be safely omitted, and it was not immediately obvious to all (it wasn't even apparent to the first author of this quotes explanation who is also a major Bash coder), it would sometimes be better to write the solution with quotes as [ -z "${var+x}" ], at the very small possible cost of an O(1) speed penalty. The first author also added this as a comment next to the code using this solution giving the URL to this answer, which now also includes the explanation for why the quotes can be safely omitted.

(Often) The wrong way

if [ -z "$var" ]; then echo "var is blank"; else echo "var is set to '$var'"; fi

This is often wrong because it doesn't distinguish between a variable that is unset and a variable that is set to the empty string. That is to say, if var='', then the above solution will output "var is blank".

The distinction between unset and "set to the empty string" is essential in situations where the user has to specify an extension, or additional list of properties, and that not specifying them defaults to a non-empty value, whereas specifying the empty string should make the script use an empty extension or list of additional properties.

The distinction may not be essential in every scenario though. In those cases [ -z "$var" ] will be just fine.



Related Topics



Leave a reply



Submit