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

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.

Related

Using a string value with the name of an existing variable to get the value of the existing variable

I am using a bash script in an azure devops pipeline where a variable is created dynamically from one of the pipeline tasks.
I need to use the variable's value in subsequent scripts, I can formulate the string which is used for the variable name, however cannot get the value of the variable using this string. I hope the below example makes it clear on what I need.
Thanks in advance for your help.
PartAPartB="This is my text"
echo "$PartAPartB" #Shows "This is my text" as expected
#HOW DO I GET BELOW TO PRINT "This is my text"
#without using the PartAPartB variable and
#using VarAB value to become the variable name
VarAB="PartAPartB"
VARNAME="$VarAB"
echo $("$VARNAME") #INCORRECT
You can use eval to do this
$ PartAPartB="This is my text"
$ VarAB="PartAPartB"
$ eval "echo \${${VarAB}}"
This is my text
You have two choices with bash. Using a nameref with declare -n (which is the preferred approach for Bash >= 4.26) or using variable indirection. In your case, examples of both would be:
#!/bin/bash
VarAB="PartAPartB"
## using a nameref
declare -n VARNAME=VarAB
echo "$VARNAME" # CORRECT
## using indirection
othervar=VarAB
echo "${!othervar}" # Also Correct
(note: do not use ALLCAPS variable names, those a generally reserved for environment variables or system variables)

Call variable from within variable

I have a source file which contains several libraries for names of variables. For example:
qvs_var1="ABC1"
qvs_var2="LMN2"
qvs_var3="LNE5"
qvs_var4="RST2"
....
Loading in the source file at the beginning of another file with:
source lib_file.csh
I now have access to the variables listed above. I want to access them dynamically and sequentially from a file prompting the variables to process. For example:
# Load in source file
source lib_file.csh
# Read in variables to process
vars=$(<variables_to_process.txt)
# For this example, vars = var1 var3
# Begin looping
For var in ${vars}
do
echo ${qvs_${var}}
done
Where the output should be: ABC1, and then LNE5. The error in the echo line above prompts: 'bad substitution'. What is the proper format to achieve what is needed?
What you are after is called indirection:
${parameter} The value of parameter is substituted. The braces are required when parameter is a positional parameter with more than one digit, or when parameter is followed by a character which is not to be interpreted as a part of its name.
If the first character of parameter is an exclamation point (!), a level of variable indirection is introduced. Bash uses the value of the variable formed from the rest of the parameter as the name of
the variable; this variable is then expanded and that value is used in the rest of the substitution, rather than the value of parameter itself. This is known as indirect expansion. The exceptions to this are the expansions of ${!prefix*} and ${!name[#]} described below. The exclamation point must immediately follow the left brace in order to introduce indirection.
source: man bash
Below you find a simple example:
$ foo_1="car"
$ bar="1"
We are now interested in printing the value of foo_1 using bar:
$ tmpvar="foo_$bar"
$ echo ${!tmpvar}
car

Insert variable value or default value if empty in a string

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

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.

Scope of variables in KSH

I have written a sample KornShell function to split a String, put it in an array and then print out the values.
The code is as below
#!/usr/bin/ksh
splitString() {
string="abc#hotmail.com;xyz#gmail.com;uvw#yahoo.com"
oIFS="$IFS";
IFS=';'
set -A str $string
IFS="$oIFS"
}
splitString
echo "strings count = ${#str[#]}"
echo "first : ${str[0]}";
echo "second: ${str[1]}";
echo "third : ${str[2]}";
Now the echo does not print out the values of the array, so I assume it has something to do with the scope of the array defined.
I am new to Shell scripting, can anybody help me out with understanding the scope of variables in the example above?
The default scope of a variable is the whole script.
However, when you declare a variable inside a function, the variable becomes local to the function that declares it. Ksh has dynamic scoping, so the variable is also accessible in functions that are invoked by the function that declares the variable. This is tersely documented in the section on functions in the manual. Note that in AT&T ksh (as opposed to pdksh and derivatives, and the similar features of bash and zsh), this only applies to functions defined with the function keyword, not to functions defined with the traditional f () { … } syntax. In AT&T ksh93, all variables declared in functions defined with the traditional syntax are global.
The main way of declaring a variable is with the typeset builtin. It always makes a variable local (in AT&T ksh, only in functions declared with function). If you assign to a variable without having declared it with typeset, it's global.
The ksh documentation does not specify whether set -A makes a variable local or global, and different versions make it either. Under ksh 93u, pdksh or mksh, the variable is global and your script does print out the value. You appear to have ksh88 or an older version of ksh where the scope is local. I think that initializing str outside the function would create a global variable, but I'm not sure.
Note that you should use a local variable to override the value of IFS: saving to another variable is not only clumsy, it's also brittle because it doesn't restore IFS properly if it was unset. Furthermore, you should turn off globbing, because otherwise if the string contains shell globbing characters ?*\[ and one of the words happens to match one or more file on your system it will be expanded, e.g. set -A $string where string is a;* will result in str containing the list of file names in the current directory.
set -A str
function splitString {
typeset IFS=';' globbing=1
case $- in *f*) globbing=;; esac
set -f
set -A str $string
if [ -n "$globbing" ]; then set +f; fi
}
splitString "$string"
Variables are normally global to the shell they're defined in from the time they're defined.
The typeset command can make them local to the function they're defined in, or alternatively to make them automatically exported (even when they're updated.)
Read up "typeset" and "integer" in the manpage, or Korn's book.

Resources