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