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
How to Add External References in Monodevelop
Host Multiple ASP.NET Core Web Application Under a Single Linux Server
How to Run a Bash Function() in a Remote Host? in Ubuntu
Expect Script Error Send: Spawn Id Exp4 Not Open While Executing
Bluez: Setting Local Address to Be Private and Non-Resolvable
Run Random Command in Bash Script
Run Hydra (Mpiexec) Locally Gives Strange Ssh Error
Yocto for Nvidia Jetson Fails Because of Gcc 7 - Cannot Compute Suffix of Object Files
Ftrace: System Crash When Changing Current_Tracer from Function_Graph via Echo
Launching Sonar Scanner from a Gitlab Docker Runner
How to Cross-Compile a Autotools Project for Arm
Selinux Prevented Httpd(Usr/Sbin/Httpd) Write Access to /Var/Www/HTML/Bookings/Templates_C
How to Extract Value from JSON Contained in a Variable Using Jq in Bash
Suppress Error Message While Using Cat Command