Using "${a:-b}" for variable assignment in scripts
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.
excerpt
${parameter:-word}
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".
Examples
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}"
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"
0
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.
Example
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}"
default
$ echo "$VAR1"
default
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
References
- Parameter Expansions - Bash Hackers Wiki
- 10.2. Parameter Substitution
- Bash Parameter Expansions
@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
CMD
heres some stuff
one
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
CMD
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:
TOP
Here you'll set some defaults and prepare to print them when called...
#!/bin/sh
_top_of_script_pr() (
IFS="$nl" ; set -f #only split at newlines and don't expand paths
printf %s\\n ${strings}
) 3<<-TEMPLATES
${nl=
}
${PLACE:="your mother's house"}
${EVENT:="the unspeakable."}
${ACTION:="heroin"}
${RESULT:="succeed."}
${strings:="
I went to ${PLACE} and saw ${EVENT}
If you do ${ACTION} you will ${RESULT}
"}
#END
TEMPLATES
MIDDLE
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"
_top_of_script_pr
}
_less_important_function() { #...more logic...
one=2
: "${ACTION:="calligraphy"}"
_top_of_script_pr
}
BOTTOM
You've got it all setup now, so here's where you'll execute and pull your results.
_less_important_function
: "${PLACE:="the cemetery"}"
_more_important_function
: "${RESULT:="regret it."}"
_less_important_function
RESULTS
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.
HOW IT WORKS:
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:
${var_name
:=desired_value}
If instead you wish to set only an unset variable, you would omit the :colon
and null values would remain as is.
ON SCOPE:
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}.
Personal experience.
I use this format sometimes in my scripts to do ad-hoc over-riding of values, e.g. if I have:
$ cat script.sh
SOMETHING="${SOMETHING:-something}"; echo "$SOMETHING";
I can run:
$ env SOMETHING="something other than the default value" ./script.sh`
without having to change the original default value of SOMETHING
.