Insert variable value or default value if empty in a string - bash

I want to insert the value of an environment variable in a string or a default value if the corresponding variable is not initialized.
Example:
if [ -z $MY_VAR ];
then
MY_VAR="default"
fi
echo "my variable contains $MY_VAR"
I'm however using a lot of variables in my strings and the tests are cluttering my script.
Is there a way to make a ternary expression in my string?
Example of what I want to achieve (it doesn't work):
echo "my variable contains ${-z $MY_VAR ? $MY_VAR : 'default'}"

To actually set the value of the variable, rather than just expanding to a default if it has no value, you can use this idiom:
: ${MY_VAR:=default}
which is equivalent to your original if statement. The expansion has the side effect of actually changing the value of MY_VAR if it is unset or empty. The : is just the do-nothing command which provides a context where we can use the parameter expansion, which is treated as an argument that : ignores.

See Bash Default Values
→ echo "my variable contains ${MY_VAR:-default}"
my variable contains default

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.

Use variable's value to get another variable [duplicate]

I am trying to create an environment variable in bash script, user will input the name of environment variable to be created and will input its value as well.
this is a hard coded way just to elaborate my question :
#!/bin/bash
echo Hello
export varName="nameX" #
echo $varName
export "$varName"="val" #here I am trying to create an environment
#variable whose name is nameX and assigning it value val
echo $nameX
it works fine
it's output is :
Hello
nameX
val
But, I want a generic code. So I am trying to take input from user the name of variable and its value but I am having trouble in it. I don't know how to echo variable whose name is user-defined
echo "enter the environment variable name"
read varName
echo "enter the value to be assigned to env variable"
read value
export "$varName"=$value
Now, I don't know how to echo environment variable
if I do like this :
echo "$varName"
it outputs the name that user has given to environment variable not the value that is assigned to it. how to echo value in it?
Thanks
To get closure: the OP's question boils down to this:
How can I get the value of a variable whose name is stored in another variable in bash?
var='value' # the target variable
varName='var' # the variable storing $var's *name*
gniourf_gniourf provided the solution in a comment:
Use bash's indirection expansion feature:
echo "${!varName}" # -> 'value'
The ! preceding varName tells bash not to return the value of $varName, but the value of the variable whose name is the value of $varName.
The enclosing curly braces ({ and }) are required, unlike with direct variable references (typically).
See https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html
The page above also describes the forms ${!prefix#} and ${!prefix*}, which return a list of variable names that start with prefix.
bash 4.3+ supports a more flexible mechanism: namerefs, via declare -n or, inside functions, local -n:
Note: For the specific use case at hand, indirect expansion is the simpler solution.
var='value'
declare -n varAlias='var' # $varAlias is now another name for $var
echo "$varAlias" # -> 'value' - same as $var
The advantage of this approach is that the nameref is effectively just an another name for the original variable (storage location), so you can also assign to the nameref to update the original variable:
varAlias='new value' # assign a new value to the nameref
echo "$var" # -> 'new value' - the original variable has been updated
See https://www.gnu.org/software/bash/manual/html_node/Shell-Parameters.html
Compatibility note:
Indirect expansion and namerefs are NOT POSIX-compliant; a strictly POSIX-compliant shell will have neither feature.
ksh and zsh have comparable features, but with different syntax.

creating environment variable with user-defined name - indirect variable expansion

I am trying to create an environment variable in bash script, user will input the name of environment variable to be created and will input its value as well.
this is a hard coded way just to elaborate my question :
#!/bin/bash
echo Hello
export varName="nameX" #
echo $varName
export "$varName"="val" #here I am trying to create an environment
#variable whose name is nameX and assigning it value val
echo $nameX
it works fine
it's output is :
Hello
nameX
val
But, I want a generic code. So I am trying to take input from user the name of variable and its value but I am having trouble in it. I don't know how to echo variable whose name is user-defined
echo "enter the environment variable name"
read varName
echo "enter the value to be assigned to env variable"
read value
export "$varName"=$value
Now, I don't know how to echo environment variable
if I do like this :
echo "$varName"
it outputs the name that user has given to environment variable not the value that is assigned to it. how to echo value in it?
Thanks
To get closure: the OP's question boils down to this:
How can I get the value of a variable whose name is stored in another variable in bash?
var='value' # the target variable
varName='var' # the variable storing $var's *name*
gniourf_gniourf provided the solution in a comment:
Use bash's indirection expansion feature:
echo "${!varName}" # -> 'value'
The ! preceding varName tells bash not to return the value of $varName, but the value of the variable whose name is the value of $varName.
The enclosing curly braces ({ and }) are required, unlike with direct variable references (typically).
See https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html
The page above also describes the forms ${!prefix#} and ${!prefix*}, which return a list of variable names that start with prefix.
bash 4.3+ supports a more flexible mechanism: namerefs, via declare -n or, inside functions, local -n:
Note: For the specific use case at hand, indirect expansion is the simpler solution.
var='value'
declare -n varAlias='var' # $varAlias is now another name for $var
echo "$varAlias" # -> 'value' - same as $var
The advantage of this approach is that the nameref is effectively just an another name for the original variable (storage location), so you can also assign to the nameref to update the original variable:
varAlias='new value' # assign a new value to the nameref
echo "$var" # -> 'new value' - the original variable has been updated
See https://www.gnu.org/software/bash/manual/html_node/Shell-Parameters.html
Compatibility note:
Indirect expansion and namerefs are NOT POSIX-compliant; a strictly POSIX-compliant shell will have neither feature.
ksh and zsh have comparable features, but with different syntax.

Strange shell behaviour

Here is a simple bash script:
a="asd"
b="qf"
echo "$a.$b"
echo "$a_$b"
It's output is:
asd.qf
qf
Why the second line is not "asd_qf" but "qf"?
Because you haven't defined a variable named a_. For that second printout to work, use:
echo "${a}_$b"
Your second echo displays the value of variable $a_ which is unset.
Use echo "${a}_$b"
The shell has rules about what can go in a variable name, and $a_ is interpreted as the variable named a_ (there is no variable with that name so its value is empty).
You can always add braces to be explicit. In this case, ${a}_$b will clearly identify what the variable name is and the result will be what you expect.

Resources