What does the special form ${varname:-text} do in shell script? - shell

I came across this expression ${varname:-text},And 3 other similar forms:
${varname-text}
${varname:=text}
${varname:?text}
It seems like text substitution, how to use this expression?

The standards document for the POSIX shell explains these special parameter expansion formats thusly:
${parameter:-word}
Use Default Values. If parameter is unset or null, the expansion of
word shall be substituted; otherwise, the value of parameter shall be
substituted.
${parameter:=word}
Assign Default Values. If parameter is unset or null, the expansion of
word shall be assigned to parameter. In all cases, the final value of
parameter shall be substituted. Only variables, not positional
parameters or special parameters, can be assigned in this way.
${parameter:?[word]}
Indicate Error if Null or Unset. If parameter is unset or null, the
expansion of word (or a message indicating it is unset if word is
omitted) shall be written to standard error and the shell exits with a
non-zero exit status. Otherwise, the value of parameter shall be
substituted. An interactive shell need not exit.
There are examples given there and a more complete table. All POSIX-compliant shells must support them. Some, like bash, include additional formats.

Related

Is this if condition equivalent to testing the existence of a variable? [duplicate]

This question already has answers here:
What does the '-' (dash) after variable names do here?
(3 answers)
Closed 2 years ago.
I'm digging around in some the system files on my Mac. In /etc/profile I've found the following excerpt:
if [ "${BASH-no}" != "no" ]; then
[ -r /etc/bashrc ] && . /etc/bashrc
fi
I've tried echo "${x-no}" for various choices of x and it seems like it's printing the value of x whenever x exists (i.e. has been set), and no otherwise.
Which leads me to wonder: Is this condition simply testing whether the variable x has been set?
Further questions: What exactly does - do? Is there a better way to test whether a variable has been set?
The meaning of ${BASH-no} is documented in §2.6.2, Parameter Expansion, of the Single Unix Specification:
${parameter:-[word]}
Use Default Values. If parameter is unset or null, the expansion of word (or an empty string if word is omitted) shall be substituted; otherwise, the value of parameter shall be substituted.
The meaning when the colon is omitted (as in your example) is described slightly later:
In the parameter expansions shown previously, use of the in the format shall result in a test for a parameter that is unset or null; omission of the shall result in a test for a parameter that is only unset.
Then there is a table, which may be easier to understand. Here are the relevant rows:
parameterSet and Not Null
parameterSet but Null
parameterUnset
${parameter:-word}
substitute parameter
substitute word
substitute word
${parameter-word}
substitute parameter
substitute null
substitute word
Here is a reliable, portable way to check whether a variable is not set at all. Note that I am using a + modifier instead of a - modifier in the parameter expansion:
if [ "${BASH+set}" = "" ]; then
echo 'BASH not set at all'
else
echo 'BASH is set, perhaps to the empty string'
fi
The expansion of "${BASH+set}" can only be "" if BASH is entirely unset. If BASH is set, even to the empty string, then "${BASH+set}" expands to "set" instead.
Is this condition simply testing whether the variable x has been set?
Yes, though it gets confused in the unlikely event that BASH=no.
What exactly does - do?
Here's man bash:
[...] Omitting the colon results in a test only for a parameter that is un‐set.
${parameter:-word}
Use Default Values. If parameter is unset or null (see above),
the expansion of word is substituted. Otherwise, the
value of parameter is substituted.
Is there a better way to test whether a variable has been set?
Yes: [ -v BASH ]. However, this is bash specific, so it defeats the purpose of checking if the current shell is bash before doing bash specific operations.

What is the purpose of setting a variable default to empty in bash?

In general, this syntax is used to guarantee a value, potentially a default argument.
(from the Bash reference manual)
${parameter:-word}
If parameter is unset or null, the expansion of word is substituted.
Otherwise, the value of parameter is substituted.
What would be the purpose of defaulting a variable to empty if the substitution is only chosen when the variable is empty anyway?
For reference, I'm looking at /lib/lsb/init-functions.
"Null" means the variable has a value, and this value is an empty string. The shell knows the variable exists.
"Unset" means the variable has not been defined : it does not exist as far as the shell is concerned.
In its usual mode, the shell will expand null and unset variable to an empty string. But there is a mode (set -u) that allows the shell to throw a runtime error if a variable is expanded when it is unset. It is good practice to enable this mode, because it is very easy to simply mis-type a variable name and get difficult to debug errors.
It can actually be useful from a computing perspective to differentiate between unset and empty variables, you can assign separate semantics to each case. For instance, say you have a function that may receive an argument. You may want to use a (non-null) default value if the parameter is unset, or any value passed to the function (including an empty string) if the parameter is set. You would do something like :
my_function()
{
echo "${1-DEFAULT_VALUE}"
}
Then, the two commands below would provide different outputs:
my_function # Echoes DEFAULT_VALUE
my_function "" # Echoes an empty line
There is also a type of expansion that does not differentiate between null and not set :
"${VAR:-DEFAULT_VALUE}"
They are both useful depending on what you need.
The way to test if a variable is set or not (without running the risk of a runtime error) is the following type of expansion :
"${VAR+VALUE}"
This will expand to an empty string if VAR is unset, or to VALUE if it is set (empty or with a value). Very useful when you need it.
Generally, it is helpful to:
Declare variables explicitely
set -u to prevent silent expansion failure
Explicitly handle unset variables through the appropriate expansion
This will make your scripts more reliable, and easier to debug.

What does the POSIX spec mean when it says this is necessary to avoid ambiguity?

When responding to this comment:
Now I got the the two ":"s are independent, and that's why I couldn't find any document about them. Is the first one needed in this case?
I noticed this paragraph in the spec for the first time:
In the parameter expansions shown previously, use of the <colon> in the format shall result in a test for a parameter that is unset or null; omission of the <colon> shall result in a test for a parameter that is only unset. If parameter is '#' and the colon is omitted, the application shall ensure that word is specified (this is necessary to avoid ambiguity with the string length expansion).
I've seen the matching explanation in the bash reference manual:
When not performing substring expansion, using the form described below (e.g., ‘:-’), Bash tests for a parameter that is unset or null. Omitting the colon results in a test only for a parameter that is unset. Put another way, if the colon is included, the operator tests for both parameter’s existence and that its value is not null; if the colon is omitted, the operator tests only for existence.
before and I understand what the difference is with the colon versions of these expansions.
What confused me just now is this sentence from the spec:
If parameter is '#' and the colon is omitted, the application shall ensure that word is specified (this is necessary to avoid ambiguity with the string length expansion).
I don't understand what ambiguity is possible here if word is unspecified.
None of the expansion sigils are valid in shell variable names so they cannot possibly start a single-character variable name. If they could then using a parameter of # would always be ambiguous without a colon since you could never tell if ${#+foo} meant the length of the variable foo or an alternate expansion on #, etc.
What am I missing here? What ambiguity requires ensuring that word exist? (I mean not having word in this expansion is clearly not useful but that's not the same thing.)
- is also a shell special parameter, whose value is a string indicating which shell options are currently set. For example,
$ echo $-
himBH
${#parameter} is the syntax for the length of a parameter.
$ foo=bar
$ echo ${#foo}
3
The expression ${#-}, therefore is ambiguous: is it the length of the value of $-, or is does it expand to the empty string if $# is empty? (Unlikely, since $# is always an integer and cannot be unset, but syntactically legal.) I interpret the spec to meant that ${#-} should resolve the ambiguity by expanding to the length of $- (which is what most shells seem to do).

bash: "${A:-B}" operator

I've been refactoring some bash code, and stumbled upon this bash notation:
"${string_a:-string_b}"
I've played a little with this on the command line:
$ echo "${string_a:-string_b}"
string_b
$ export string_a=string_a_value
$ echo "${string_a:-string_b}"
string_a_value
I seems that the {a:-b} notation returns the value of variable a if it is defined, or the string b otherwise.
Where can I find a more formal definition for this operator?
Peer pressure, I post my comment as an answer : )
I like this reference card: Advanced Bash-Scripting Guide , specifically in your case it will be useful "# Table B-4. Parameter Substitution and Expansion".
I do not copy any issue they indicate not to violate any copyright. Just find all information there.
Another useful link is the Shell Parameter Expansion section in the Bash Reference
Manual. The :- operator is defined as:
${parameter:-word} If parameter is unset or null, the expansion of
word is substituted. Otherwise, the value of parameter is substituted.
By the way, bash features three similar operators ${parameter:=word}, ${parameter:?word} and ${parameter:+word}, defined in that section.
You can access bash documentation using man bash. To search type /
${parameter:-word}
Use Default Values. If parameter is unset or null, the expansion of word is substituted. Otherwise, the value of parameter is substituted.

Bash script what is := for?

Does anyone know what is := for?
I tried googling but it seems google filters all symbol?
I know the below is something like checking if the variable HOME is a directory and then something is not equal to empty string.
if [ "${HOME:=}" != "" ] && [ -d ${HOME} ]
From Bash Reference Manual:
${parameter:=word}
If parameter is unset or null, the expansion of word is assigned to
parameter. The value of parameter is
then substituted. Positional
parameters and special parameters may
not be assigned to in this way.
Basically it will assign the value of word to parameter if and only if parameter is unset or null.
From the Bash man page:
Assign Default Values. If
parameter is unset or null, the
expansion of word is assigned to
parameter. The value of parameter
is then substituted. Positional
parameters and special parameters may
not be assigned to in this way.
Man pages are a wonderful thing. man bash will tell you almost everything you want to know about Bash.

Resources