Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems. It's 100% free, no registration required.

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

I have been looking at a few scripts other people wrote (specifically Red Hat), and a lot of their variables are assigned using the following notation VARIABLE1="${VARIABLE1:-some_val}" or some expand other variables VARIABLE2="${VARIABLE2:-`echo $VARIABLE1`}"

What is the point of using this notation instead of just declaring the values directly (e.g., VARIABLE1=some_val)?

Are there benefits to this notation or possible errors that would be prevented?

Does the :- have specific meaning in this context?

share|improve this question
up vote 77 down vote accepted

This technique allows for a variable to be assigned a value if another variable is either empty or is undefined. NOTE: This "other variable" can be the same or another variable.


    If parameter is unset or null, the expansion of word is substituted. 
    Otherwise, the value of parameter is substituted.

NOTE: This form also works, ${parameter-word}. If you'd like to see a full list of all forms of parameter expansion available within Bash then I highly suggest you take a look at this topic in the Bash Hacker's wiki titled: "Parameter expansion".


variable doesn't exist
$ echo "$VAR1"

$ VAR1="${VAR1:-default value}"
$ echo "$VAR1"
default value
variable exists
$ VAR1="has value"
$ echo "$VAR1"
has value

$ VAR1="${VAR1:-default value}"
$ echo "$VAR1"
has value

The same thing can be done by evaluating other variables, or running commands within the default value portion of the notation.

$ VAR2="has another value"
$ echo "$VAR2"
has another value
$ echo "$VAR1"


$ VAR1="${VAR1:-$VAR2}"
$ echo "$VAR1"
has another value

More Examples

You can also use a slightly different notation where it's just VARX=${VARX-<def. value>.

$ echo "${VAR1-0}"
has another value
$ echo "${VAR2-0}"
has another value
$ echo "${VAR3-0}"

In the above $VAR1 & $VAR2 were already defined with the string "has another value" but $VAR3 was undefined, so the default value was used instead, 0.

Another Example

$ VARX="${VAR3-0}"
$ echo "$VARX"

Checking and assigning using := notation

Lastly I'll mention the handy operator, :=. This will do a check and assign a value if the variable under test is empty or undefined.


Notice that $VAR1 is now set. The operator := did the test and the assignment in a single operation.

$ unset VAR1
$ echo "$VAR1"

$ echo "${VAR1:=default}"
$ echo "$VAR1"

However if the value is set prior, then it's left alone.

$ VAR1="some value"
$ echo "${VAR1:=default}"
some value
$ echo "$VAR1"
some value

Handy Dandy Reference Table

    ss of table


share|improve this answer
Note that not all of these are expansions are documented in bash. The ${var:-word} one in the Q is, but not the ${var-word} above. The POSIX documentation has a nice table though, might be worth copying it into this answer - pubs.opengroup.org/onlinepubs/9699919799/utilities/… – Graeme Apr 3 '14 at 1:02
@Graeme - thanks, added. I also found this link which had a very nice list of all the forms of parameter expansion: Parameter Expansions - Bash Hackers Wiki – slm Apr 3 '14 at 1:07
@Graeme, it is documented, you just need to pay attention to the Omitting the colon results in a test only for a parameter that is unset. – Stéphane Chazelas Dec 22 '15 at 11:31

@slm has already included the POSIX docs - which are very helpful - but they don't really expand on how these parameters can be combined to affect one another. There is not yet any mention here of this form:

${var?if unset parent shell dies and this message is output to stderr}

This is an excerpt from another answer of mine, and I think it demonstrates very well how these work:

    sh <<-\CMD
    _input_fn() { set -- "$@" #redundant
            echo ${*?WHERES MY DATA?}
            #echo is not necessary though
            shift #sure hope we have more than $1 parameter
            : ${*?WHERES MY DATA?} #: do nothing, gracefully
    _input_fn heres some stuff
    _input_fn one #here
    # shell dies - third try doesnt run
    _input_fn you there?
    # END
heres some stuff
sh: line :5 *: WHERES MY DATA?

Another example from same:

    sh <<-\CMD
    N= #N is NULL
    _test=$N #_test is also NULL and
    v="something you would rather do without"    
    ( #this subshell dies
        echo "v is ${v+set}: and its value is ${v:+not NULL}"
        echo "So this ${_test:-"\$_test:="} will equal ${_test:="$v"}"
        ${_test:+${N:?so you test for it with a little nesting}}
        echo "sure wish we could do some other things"
    ( #this subshell does some other things 
        unset v #to ensure it is definitely unset
        echo "But here v is ${v-unset}: ${v:+you certainly wont see this}"
        echo "So this ${_test:-"\$_test:="} will equal NULL ${_test:="$v"}"
        ${_test:+${N:?is never substituted}}
        echo "so now we can do some other things" 
    #and even though we set _test and unset v in the subshell
    echo "_test is still ${_test:-"NULL"} and ${v:+"v is still $v"}"
    # END
v is set: and its value is not NULL
So this $_test:= will equal something you would rather do without
sh: line 7: N: so you test for it with a little nesting
But here v is unset:
So this $_test:= will equal NULL
so now we can do some other things
_test is still NULL and v is still something you would rather do without

The above example takes advantage of all 4 forms of POSIX parameter substitution and their various :colon null or not null tests. There is more information in the link above, and here it is again.

Another thing that people often don't consider about ${parameter:+expansion} is how very useful it can be in a here-document. Here's another excerpt from a different answer:


Here you'll set some defaults and prepare to print them when called...

    _top_of_script_pr() ( 
        IFS="$nl" ; set -f #only split at newlines and don't expand paths
        printf %s\\n ${strings}
    ) 3<<-TEMPLATES
        ${PLACE:="your mother's house"}
        ${EVENT:="the unspeakable."}
            I went to ${PLACE} and saw ${EVENT}
            If you do ${ACTION} you will ${RESULT}


This is where you define other functions to call on your print function based on their results...

    EVENT="Disney on Ice."
    _more_important_function() { #...some logic...
        [ $((1+one)) -ne 2 ] && ACTION="remedial mathematics"
    _less_important_function() { #...more logic...
        : "${ACTION:="calligraphy"}"


You've got it all setup now, so here's where you'll execute and pull your results.

    : "${PLACE:="the cemetery"}" 
    : "${RESULT:="regret it."}" 


I'll go into why in a moment, but running the above produces the following results:

_less_important_function()'s first run:

I went to your mother's house and saw Disney on Ice.

If you do calligraphy you will succeed.

then _more_important_function():

I went to the cemetery and saw Disney on Ice.

If you do remedial mathematics you will succeed.

_less_important_function() again:

I went to the cemetery and saw Disney on Ice.

If you do remedial mathematics you will regret it.


The key feature here is the concept of conditional ${parameter} expansion. You can set a variable to a value only if it is unset or null using the form:


If instead you wish to set only an unset variable, you would omit the :colon and null values would remain as is.


You might notice that in the above example $PLACE and $RESULT get changed when set via parameter expansion even though _top_of_script_pr() has already been called, presumably setting them when it's run. The reason this works is that _top_of_script_pr() is a ( subshelled ) function - I enclosed it in parens rather than the { curly braces } used for the others. Because it is called in a subshell, every variable it sets is locally scoped and as it returns to its parent shell those values disappear.

But when _more_important_function() sets $ACTION it is globally scoped so it affects _less_important_function()'s second evaluation of $ACTION because _less_important_function() sets $ACTION only via ${parameter:=expansion}.

share|improve this answer

Personal experience: I use this format sometimes in my scripts to do ad-hoc over-riding of values, e.g. if I have SOMETHING="${SOMETHING:-something}"; echo "$SOMETHING"; in script.sh, I can run env SOMETHING="something else other than the default value the script assumes" ./script.sh without having to change the original default value of SOMETHING.

share|improve this answer

Have a look at man bash; search for the block "Parameter Expansion" (at about 28%).

These assignments are e.g. default functions: "Use the default value only if none has been set yet."

share|improve this answer

Are there benefits to this notation or possible errors that would be prevented?

This is based on the crazy idea your code can on the one hand produce missing inputs and on the other hand fix that by setting defaults without any consideration of why this would have been missing.

Looks great if you left university and blows up in peoples faces even decades later.

I never studied this stuff and I'm not sure if I made a good explanation. But I can say that from my experience these things have race conditions, a missing variable declaration can have many reasons. Trying to "default" out of it means further code is enabled to run - which will only work if that code is completely independent of the thing that caused the miss in the first place.

It's a different thing if it's done only at the initialization of the script. All fine for that.

But these "features" are sometimes used blindly it all over scripts.

I would not hate this much had it not consumed considerable amounts of my life, fixing broken servers, mis-processed data, etc. all coming down to thinking you can process what was wrong in the first place.

There's a good talk "The science of insecurity" by Meredith Petterson:


The term she uses for this kind of programming is "Shotgun parsers"...

share|improve this answer

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.