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 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).

      • Additionally, if 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.

      • 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).
  • 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.