Bash: usage of `true`
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.
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 usingtrue
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 whenset -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
(...)
aroundtrue $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).
- Additionally, if
set -u
(set -o nounset
) happens not to be in effect, the technique treats all variables as defined.
- Additionally, if
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.- As an aside:
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).
- As an aside:
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 - invokingbash
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.