How can I make bash treat undefined variables as errors?
You can use:
set -u
at the start of your script to throw an error when using undefined variables.
-u
Treat unset variables and parameters other than the special parameters "@" and "*" as an error when performing parameter expansion. If expansion is attempted on an unset variable or parameter, the shell prints an error message, and, if not interactive, exits with a non-zero status.
Proper to use unassigned variable?
It is fine unless -u
option is set.
$ unset foo
$ echo $foo
$ set -u
$ echo $foo
bash: foo: unbound variable
$
Some assign a default value as you mentioned, and some reference them like ${foo-}
to avoid that error.
Why does Bash treat undefined variables as true in an 'if' statement?
The argument to if
is a statement to execute, and its exit status is tested. If the exit status is 0
the condition is true and the statements in then
are executed.
When $CONDITION
isn't set, the statement is an empty statement, and empty statements always have a zero exit status, meaning success. So the if
condition succeeds.
This behavior is explained in the POSIX shell specification:
If there is no command name, but the command contained a command substitution, the command shall complete with the exit status of the last command substitution performed. Otherwise, the command shall complete with a zero exit status.
Bash - force error if variable not defined
Use:
set -e # Stop on error. I can't believe that this is not default.
set -u # Stop if trying to use un-initialized variables.
Why the `nounset` shell option doesn't raise an error on unset arrays?
I believe this is just a shortcoming of the documentation; it should mention array parameters index with @
and *
as well.
The intent is to make it look like an array can be "set" to an empty value. However,
foo=()
doesn't actually assign an empty array value to the name foo
. It simply sets the array attribute on the name foo
, and clears any values that may already have been present. $foo
and ${foo[0]}
are equivalent, and both will be treated as an unset parameter. Each of ${foo[@]}
and ${foo[@]}
are special cased to be "set", but "empty", as far as -u
is concerned.
Note, though, that the array attribute doesn't actually need to be set for the @
/*
indices to override -u
.
$ unset bar
$ set -o nounset
$ echo ${bar[@]}
$
Test if a variable is set in Bash when using set -o nounset
#!/bin/bash
set -o nounset
VALUE=${WHATEVER:-}
if [ ! -z ${VALUE} ];
then echo "yo"
fi
echo "whatever"
In this case, VALUE
ends up being an empty string if WHATEVER
is not set. We're using the {parameter:-word}
expansion, which you can look up in man bash
under "Parameter Expansion".
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
When Should I Use Tcp_Nodelay and When Tcp_Cork
Command Line Utility to Print Statistics of Numbers in Linux
Bash Copy All Files Except One
Grep Without Showing Path/File:Line
Search and Replace with Sed When Dots and Underscores Are Present
How to Set Rpath and Runpath with Gcc/Ld
Install Mono and Monodevelop on Centos 5.X/6.X
Docker-Compose Up and User Inputs on Stdin
Prevent File Descriptors Inheritance During Linux Fork
Difference Between Shell and Environment Variables
Linux: Remove File Extensions for Multiple Files
How to Find the Maximum Stack Size
How to Extract Only the Raw Contents of an Elf Section
Making Cmake Print Commands Before Executing