@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}.