1283

I have a whole bunch of tests on variables in a bash (3.00) shell script where if the variable is not set, then it assigns a default, e.g.:

if [ -z "${VARIABLE}" ]; then 
    FOO='default'
else 
    FOO=${VARIABLE}
fi

I seem to recall there's some syntax to doing this in one line, something resembling a ternary operator, e.g.:

FOO=${ ${VARIABLE} : 'default' }

(though I know that won't work...)

Am I crazy, or does something like that exist?

llrs
  • 3,308
  • 35
  • 68
Edward Q. Bridges
  • 16,712
  • 8
  • 35
  • 42
  • 1
    Shell Parameter Expansion section of the bash shell reference is a good starting places: https://tiswww.case.edu/php/chet/bash/bashref.html#Shell-Expansions. – Kemin Zhou Jul 17 '17 at 18:31
  • 2
    Gnu.org documentation for [Shell Parameter Expansion](https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html) (hint: there's a lot of other cool stuff you can do!) – JDB Jun 28 '18 at 16:55

11 Answers11

2278

Very close to what you posted, actually. You can use something called Bash parameter expansion to accomplish this.

To get the assigned value, or default if it's missing:

FOO="${VARIABLE:-default}"  # If variable not set or null, use default.
# If VARIABLE was unset or null, it still is after this (no assignment done).

Or to assign default to VARIABLE at the same time:

FOO="${VARIABLE:=default}"  # If variable not set or null, set it to default.
Matthias Braun
  • 32,039
  • 22
  • 142
  • 171
Andrew McGregor
  • 31,730
  • 2
  • 29
  • 28
  • 142
    If I wanted to find this in the documentation, what would a google for? Googling with special characters like ${:-} isn't very easy. – Joshua Olson Apr 24 '13 at 18:34
  • 45
    In response to solarmist: "bash variable assignment default" let me to this page, and searching bash's manual page with 'variable ass' got me to the right section after ~five 'next' – Mooshu Sep 19 '13 at 20:09
  • 112
    Note that it's [*not* bash-specific](http://pubs.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_02) and will work with the whole shell-family. It's safe for generic-scripts. – Jocelyn delalande Feb 03 '14 at 19:58
  • 32
    @solarmist The key word is "bash parameter expansion", if you want to find more about it and its companions. – duleshi Apr 30 '14 at 02:43
  • 10
    What is the difference between :- and := ? – Berk U. Dec 18 '14 at 19:41
  • @Jocelyndelalande I confirm: I tryed in a common profile it is sourced both by bash and zsh I use in my git versioned [home](https://github.com/fibo/home) and it works well on both. – Gianluca Casati Jun 03 '15 at 21:55
  • @Andrew the := is very clear and intuitive among many other [shell substitutions statements](http://www.tldp.org/LDP/abs/html/parameter-substitution.html). – Gianluca Casati Jun 03 '15 at 21:55
  • 5
    subjectAltName=${1:-${SUBJECT_ALT_NAME:-$defaultSubjectAltName}}; ... is the syntax for 3 alternatives ... here the first default is the first param passed to the script, else an env var is used else a script var is used – danday74 Jan 22 '16 at 19:54
  • 31
    Here's a more in-depth explanation on the topic http://unix.stackexchange.com/a/122848/2407 – jottr Jan 29 '16 at 23:27
  • 5
    This page has a nice cheat sheet explaining all of these: http://stackoverflow.com/a/16753536/5359531 – user5359531 May 18 '16 at 01:26
  • Is it possible to use shell special variables like `${@:-default}` Doesn't seem to work – Tofandel Apr 26 '21 at 14:25
  • 1
    @JoshuaOlson: example how to google: https://www.google.com/search?client=firefox-b-lm&q=difference+%24%7Bparameter%3A%3Dword%7D+and+%24%7Bparameter%3A-word%7D – Eugen Konkov Aug 28 '22 at 10:56
  • Worth clarifying that "null" in bash documentation simply means empty string. (quite confusing jargon to people coming from other languages, as it sounds like NULL or nil singleton values that are distinct from strings; but no, in bash it refers to empty string which is a regular value, whereas variable "not set" at all is the distinct state) – Beni Cherniavsky-Paskin Feb 06 '23 at 16:23
  • @Jocelyndelalande So the name "Bash parameter expansion" is misleading and the whole thing is POSIX compliant. – Martin Braun Feb 16 '23 at 15:14
  • nested fallbacks are also possible: `echo "${FOO:-${BAR:-fallback value}}"` – Abdull May 12 '23 at 11:02
581

For command line arguments:

VARIABLE="${1:-$DEFAULTVALUE}"

which assigns to VARIABLE the value of the 1st argument passed to the script or the value of DEFAULTVALUE if no such argument was passed. Quoting prevents globbing and word splitting.

Hans Ginzel
  • 8,192
  • 3
  • 24
  • 22
miku
  • 181,842
  • 47
  • 306
  • 310
422

If the variable is same, then

: "${VARIABLE:=DEFAULT_VALUE}"

assigns DEFAULT_VALUE to VARIABLE if not defined.

  • The colon builtin (:) ensures the variable result is not executed
  • The double quotes (") prevent globbing and word splitting.

Also see Section 3.5.3, Shell Parameter Expansion, in the Bash manual.

David Cook
  • 483
  • 7
  • 25
Hans Ginzel
  • 8,192
  • 3
  • 24
  • 22
  • 44
    This tricky trick wasn't obvious to me, using the : no-effect builtin to eat the expansion of the ${..} BUT leaving VARIABLE set. Until now I was doing this: VARIABLE="${VARIABLE:-DEFAULT_VALUE}" and feeling dorky for using VARIABLE twice. – dino Oct 06 '15 at 18:06
  • 1
    It turns out THIS DOES NOT ALWAYS WORK FOR BASH-BUILTIN VARIABLES. It's very very weird. Currently those known to me are `HISTTIMEFORMAT` – Otheus Apr 13 '16 at 10:20
  • how would I set multiple default values? I tried ": ${VARIABLE:=DEFAULT_VALUE} : ${VARIABLE2:=DEFAULT_VALUE2}" but that doesnt seem to work... – Björn May 10 '16 at 11:00
  • 13
    @Hans: The colon is the command (which does nothing) and thus is needed only once. E.g. `: ${FOO:=DEFAULT1} ${BAR:=DEFAULT2}` – Patrik May 29 '16 at 15:45
  • 4
    Any chance to use something like this with `export`? I want to make this variable available to the scripts called from the current script. – Serge Rogatch Sep 29 '21 at 22:02
  • 1
    @SergeRogatch Just call `export VARIABLE` after you set it. – Sekenre Oct 12 '21 at 13:59
  • Or you can execute `set -a`, if you need to export all the variables in the script. – orgads Sep 12 '22 at 06:38
  • 1
    The good part is, this is POSIX compliant. – Martin Braun Feb 16 '23 at 15:44
126

To answer your question and on all variable substitutions

echo "${var}"
echo "Substitute the value of var."
    

echo "${var:-word}"
echo "If var is null or unset, word is substituted for var. The value of var does not change."
    

echo "${var:=word}"
echo "If var is null or unset, var is set to the value of word."
    

echo "${var:?message}"
echo "If var is null or unset, message is printed to standard error. This checks that variables are set correctly."
    

echo "${var:+word}"
echo "If var is set, word is substituted for var. The value of var does not change."

You can escape the whole expression by putting a \ between the dollar sign and the rest of the expression.

echo "$\{var}"
Pierre-Antoine Guillaume
  • 1,047
  • 1
  • 12
  • 28
Guru
  • 1,653
  • 1
  • 7
  • 14
  • 3
    There's also `{var-default}` where `default` will be used only when `var` is undefined. If `var` is defined but null, `default` won't be used. – matt Oct 11 '20 at 20:02
  • 6
    Why is there a a backslash in each line? Other answers don't have it. – Asclepius Nov 11 '20 at 03:07
  • The `"${var:?message}"` can be super-helpful later on when you're stuck with only the output as log for, let's say, why some CI script failed for example: Instead of `MyCmd --key ${API_KEY}` erroring with (let's pretend): "MyCmd [ERROR] argument required", if you instead just do `MyCmd --key ${API_KEY:?}`, everyone can see an understandable error: `sh: API_KEY: parameter not set` and if `set -e`, the script will exit with error immediately – conny Jun 09 '23 at 03:38
  • `echo "$\{var}"` produces `$\{var}`, maybe you want `echo "\${var}"` which produces `${var}`. – Doncho Gunchev Jul 06 '23 at 08:29
61

Even you can use like default value the value of another variable

having a file defvalue.sh

#!/bin/bash
variable1=$1
variable2=${2:-$variable1}

echo $variable1
echo $variable2

run ./defvalue.sh first-value second-value output

first-value
second-value

and run ./defvalue.sh first-value output

first-value
first-value
Montells
  • 6,389
  • 4
  • 48
  • 53
41

see here under 3.5.3(shell parameter expansion)

so in your case

${VARIABLE:-default}
Clément
  • 12,299
  • 15
  • 75
  • 115
ghostdog74
  • 327,991
  • 56
  • 259
  • 343
41

FWIW, you can provide an error message like so:

USERNAME=${1:?"Specify a username"}

This displays a message like this and exits with code 1:

./myscript.sh
./myscript.sh: line 2: 1: Specify a username

A more complete example of everything:

#!/bin/bash
ACTION=${1:?"Specify 'action' as argv[1]"}
DIRNAME=${2:-$PWD}
OUTPUT_DIR=${3:-${HOMEDIR:-"/tmp"}}

echo "$ACTION"
echo "$DIRNAME"
echo "$OUTPUT_DIR"

Output:

$ ./script.sh foo
foo
/path/to/pwd
/tmp

$ export HOMEDIR=/home/myuser
$ ./script.sh foo
foo
/path/to/pwd
/home/myuser
  • $ACTION takes the value of the first argument, and exits if empty
  • $DIRNAME is the 2nd argument, and defaults to the current directory
  • $OUTPUT_DIR is the 3rd argument, or $HOMEDIR (if defined), else, /tmp. This works on OS X, but I'm not positive that it's portable.
Curtis Mattoon
  • 4,642
  • 2
  • 27
  • 34
31

It is possible to chain default values like so:

DOCKER_LABEL=${GIT_TAG:-${GIT_COMMIT_AND_DATE:-latest}}

i.e. if $GIT_TAG doesnt exist, take $GIT_COMMIT_AND_DATE - if this doesnt exist, take "latest"

InsOp
  • 2,425
  • 3
  • 27
  • 42
22

Then there's the way of expressing your 'if' construct more tersely:

FOO='default'
[ -n "${VARIABLE}" ] && FOO=${VARIABLE}
Rob Wells
  • 36,220
  • 13
  • 81
  • 146
13

Here is an example

#!/bin/bash

default='default_value'
value=${1:-$default}

echo "value: [$value]"

save this as script.sh and make it executable. run it without params

./script.sh
> value: [default_value]

run it with param

./script.sh my_value
> value: [my_value]
Assaf Shomer
  • 1,429
  • 1
  • 15
  • 21
2

If you want 1 liner for your if-then-else, then we can consider rewriting:

if [ -z "${VARIABLE}" ]; then 
    FOO='default'
else 
    FOO=${VARIABLE}
fi

with semicolons:

if [ -z ${VARIABLE} ]; then FOO=`default`; else FOO=${VARIABLE}; fi

Alternatively, you can drop the if-then-else-fi keywords if you use boolean operators such as:

[ -z "${VARIABLE}" ] && FOO='default' || FOO=${VARIABLE}

Generalizing, the boolean operator pattern is:

conditional && then_command || else_command
Stephen Quan
  • 21,481
  • 4
  • 88
  • 75