Reputation: 10483
In many scripts I've inherited from a former employee I keep seeing this pattern:
if (true $SOME_VAR)&>/dev/null; then
...
fi
or this one
(true $SOME_VAR)&>/dev/null || SOME_VAR="..."
The man page for true
says it always returns true, hence I keep wondering, what is the point of these checks? In the first case the then
part is always executed, in the second case the right hand part is never executed.
Upvotes: 21
Views: 1464
Reputation: 437090
To complement jwodder's helpful answer and Fred's helpful answer:
In Bash v4.2+
, the less obscure and more efficient -v
operator can be used to test if a variable is defined[1] (note that no $
must be used):
[[ -v SOME_VAR ]]
In older Bash versions and in POSIX-compliant scripts, use Fred's parameter-expansion-based approach, which is also more efficient than the (true ...)
approach.
If the intent is to simply provide a default value, as in the (true $SOME_VAR)&>/dev/null || SOME_VAR="..."
idiom, use the (POSIX-compliant) technique suggested by kojiro, also based on a parameter expansion:
SOME_VAR=${SOME_VAR-...} # keep $SOME_VAR value or default to '...'
Toby Speight suggests another POSIX-compliant variant, ${SOME_VAR=...}
, which directly updates the variable with the default value, if it is undefined; however, it has the side effect of expanding to the (resulting) value - which may or may not be desired. A concise, but also slightly obscure way to suppress the expansion is to pass the expansion to the colon (null) utility (:
), which expands, but otherwise ignores its arguments (compared to using true
for the same purpose, it is perhaps slightly less confusing):
: ${SOME_VAR=...} # set $SOMEVAR to '...' only if not defined
Note that all parameter expansions shown/mentioned above have a variant that places :
before the operator, which then acts not only when the variable is undefined, but also when it is defined but empty (contains the null string):
${SOME_VAR:+...}
, ${SOME_VAR:-...}
, ${SOME_VAR:=...}
Arguably, this variant behavior is the generally more robust technique, especially given that when set -u
(set -o nunset
) is not turned on, undefined variables expand to the null (empty) string.
To add to jwodder's explanation:
The use of (...)
around true $SOME_VAR
to create a subshell is crucial for this somewhat obscure test for variable existence to work as intended.
Without a subshell, the entire script would abort.
The need for a subshell makes the technique not just obscure, but also inefficient (although that won't really be noticeable with occasional use).
set -u
(set -o nounset
) happens not to be in effect, the technique treats all variables as defined.With the subshell, only the subshell aborts, which is reflected in its exit code to the current shell: 1
, if the subshell aborted (the variable doesn't exist), 0
otherwise.
Therefore, the (true ...)
command only evaluates to (conceptually) true if the variable exists.
&>/dev/null
suppresses the error message from the subshell that is emitted if the variable doesn't exist.
true
never produces no output, so it is sufficient to use (true $SOME_VAR)2>/dev/null
(suppress stderr only) - this change makes the technique POSIX-compliant (though still not advisable).It isn't just set -u
(set -o nounset
) statements inside a script that turn on aborting in case of access to an undefined variable - invoking bash
explicitly with command-line option -u
has the same effect.
[1] Since Bash v4.3, you can also test whether an array variable has an element with the specified index; e.g.:
a=( one two ); [[ -v a[0] ]]
succeeds, because an array element with index 0
exists; works analogously with associative arrays.
Upvotes: 14
Reputation: 43188
While not strictly the same,
if [ x"$SOME_VAR" = x ]; then
...
fi
tends to do what you want; that is the if is true if $SOME_VAR
is undefined or (difference:) defined to be the zero-length string.
This code does not work if SOME_VAR
is unset and -u
is set. I believe the following bashism works though: "${SOME_VAR-}" = ""
.
Upvotes: 3
Reputation: 6995
The following is probably equivalent, and more straightforward :
if [ "${SOME_VAR+x}" ] then
...
fi
Or, in the assignment case :
[ "${SOME_VAR+x}" ] || SOME_VAR="..."
The +
expansion operator expands to a null string if the variable is unset, and to x
if it is assigned (assigned a null string still means assigned). In this case, you could replace x
by whatever you want (except a null string).
There is also a ${SOME_VAR:+x}
variant. The difference is with null strings : :+
expands to a null string if the variable is assigned a null string (while +
expands to x
if the value is assigned, even if it is a null string).
Upvotes: 6
Reputation: 57460
If set -u
(a.k.a. set -o nounset
) is in effect, true $SOME_VAR
will fail when $SOME_VAR
is not defined. This is therefore a way to test whether the variable is defined.
Upvotes: 24