Usage of :- (colon dash) in bash

Solution 1:

If $PUBLIC_INTERFACE exists and isn't null, return its value, otherwise return "eth0".

There are actually a few of these documented in the bash man page:

${parameter:-word} Use Default Values. If parameter is unset or null, the expansion of word is substituted. Otherwise, the value of parameter is substituted.

${parameter:=word} Assign Default Values. 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.

${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.

${parameter:+word} Use Alternate Value. If parameter is null or unset, nothing is substituted, otherwise the expansion of word is substituted.

Solution 2:

:- is used in the ${parameter:-word} shell parameter expansion: if parameter is null or unset, it expands to the value of word, otherwise to the value of parameter.

Example:

$ str=
$ echo "${str:-default}"
default

This and the similar expansions using :=, :+ and :? all come in two flavours: with and without a colon. The difference is that the expansion with the colon kicks in for "unset or null", whereas without the colon, it's just "unset".

Observe:

$ str=                      # Null, but not unset
$ echo "${str-default}"     # Expands to value of $str, the empty string
    
$ echo "${str:-default}"    # Expands to "default"
default

Where is this useful? A few examples:

  • Default values

    • The editor invoked to edit the last command with fc is the result of the expansion ${FCEDIT:-${EDITOR:-vi}}: $FCEDIT if defined and not null, or else $EDITOR if defined and not null, or else vi.

    • A loop in a script that should read from a file if one is provided as an argument and from standard input otherwise could look like this:

      while IFS= read -r line; do
          # do something
      done < "${1:-/dev/stdin}"
      
  • When using set -u

    set -u is a handy way to force cleaner scripting by having the script die when encountering an unset variable, as promoted by for example this article (not that I endorse everything in there1). If we want to check if a certain variable has a value with [[ $var ]], the script now dies if var is unset, even though this might be legitimate.

    The way around this is using [[ ${var:-} ]] instead, and set -u won't complain. (This is basically using a default value again, but the substituted value is the empty string in this case.2)

These expansions are not exclusive to Bash, by the way: the POSIX shell spec has them all, too.


1 See also BashFAQ/112, What are the advantages and disadvantages of using set -u (or set -o nounset)?

2 Just ${var-} instead of ${var:-} would actually be sufficient: set -u doesn't care about variables that have been set but contain the empty string; only unset ones trigger an error.