bash expanding value of a variable to use in another variable - bash

I hope the question makes sense, I would like to do something like:
a=test
b=a
echo ${$b} # should echo test
Basically I'd like $b to expand to the value a and have bash echo out the value of variable $a (since ${a} is test).
What syntax should I use?

a=test
b=a
echo ${!b} # does echo test

Related

Update value of a variable from a function in shell script

a=master
b="9876"
secondfunction(){
a=develop
b="1234"
echo "Inside second function"
echo $1
echo $2
}
secondfunction $a $b
In the above shell script echo command prints a=master and b=9876. I want to print a=develop and b =1234.
Here you are indeed changing the value of a and b like you want to, To see this you can print out a and b after the function has been called
echo $a $b
secondfunction $a $b
echo $a $b
and you can see that the output will be master 9876 for the first one and develop 1234 for the second one. Just that you cannot change the value of $1 and $2 by changing the values of a and b. For that you'll need to explicitly change them using set like #william has pointed out.
Also variables in shell script are globally scoped unless explicitly mentioned otherwise. So if all you want to do is change the values of those variables, you need not explicitly pass them into the function as arguments.
a=master
b="9876"
secondfunction(){
a=develop
b="1234"
echo "Inside second function"
echo $a
echo $b
}
secondfunction
will also work

Bash substitution inside substitution

I am learning bash, and wanted to do something very simple, here's my script:
#!/bin/bash
#read-multiple: reads multiple values from keyboard
echo -n "Enter one or more values:"
read var1 var2 var3 var4 var5
for i in {1..5}
do
echo var$i= ${var"$i"}
done
In the for loop I am trying to print to values entered by the user, only at the echoline I get the error:
${var"$i"}: bad substitution
What I was expecting to happen is:
$i expands to the current value between 1 and 5 (say 1 for example)
${var"$i"} expands to ${var1} which expands to the value of var1
This is not the case apparently...Could you explain to me why that is ? does bash expand everything on the line at once?
I have also tried ${var${$i}} and $var${$i} but both give the same error...why is that ?
You could do this:
for v in var{1..5}; do
echo $v = ${!v}
done
or
for i in {1..5}; do
v="var$i"
echo $v = ${!v}
done
See this post:
What is indirect expansion? What does ${!var*} mean?
Documentation here:
Shell Parameter Expansion

In a function Bash: how to check if an argument is a set variable?

I want to implement a bash function which test is the 1st argument is actually a variable, defined somewhere.
For instance, in my .bashrc :
customPrompt='yes';
syntaxOn='no';
[...]
function my_func {
[...]
# I want to test if the string $1 is the name of a variable defined up above
# so something like:
if [[ $$1 == 'yes' ]];then
echo "$1 is set to yes";
else
echo "$1 is not set or != to yes";
fi
# but of course $$1 doesn't work
}
output needed :
$ my_func customPrompt
> customPrompt is set to yes
$ my_func syntaxOn
> syntaxOn is set but != to yes
$ my_func foobar
> foobar is not set
I tried a lot of test, like -v "$1", -z "$1", -n "$1", but all of them test $1 as a string not as a variable.
(please correct me if I make not myself clear enought)
In the bash you can use the indirect variable subtituion.
t1=some
t2=yes
fufu() {
case "${!1}" in
yes) echo "$1: set to yes. Value: ${!1}";;
'') echo "$1: not set. Value: ${!1:-UNDEF}";;
*) echo "$1: set to something other than yes. Value: ${!1}";;
esac
}
fufu t1
fufu t2
fufu t3
prints
t1: set to something other than yes. Value: some
t2: set to yes. Value: yes
t3: not set. Value: UNDEF
The ${!variablename} in bash mean indirect variable expansion. Described in the e.g. https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html
Whrere:
The basic form of parameter expansion is ${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 that is not to be interpreted as 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 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.
Also, check this: https://stackoverflow.com/a/16131829/632407 how to modify in a function a value of the variable passed indirectly.
You can check variable set or not by simply like
if [[ $var ]]
then
echo "Sorry First set variable"
else
echo $var
fi
You can do something like this for your script
customPrompt='yes';
syntaxOn='no';
function my_func
{
if [[ ${!1} ]];then
echo "$1 is set to ${!1}";
else
echo "$1 is not set";
fi
}
my_func customPrompt
my_func syntaxOn
my_func foobar
Output:
customPrompt is set to yes
syntaxOn is set to no
foobar is not set
You can customize the function as per you requirement by simply making some comparison conditions.
For more details you can check this answer
If you really want to check if your variable is set or unset (not just empty), use this format:
function my_func {
if [[ -z ${!1+.} ]]; then
echo "$1 is not set."
elif [[ ${!1} == yes ]]; then
echo "$1 is set to yes"
else
echo "$1 is set to \"${!1}\"."
fi
}
You're going to have problems...
The Bash shell is a very wily creature. Before you execute anything, Bash comes in and interpolates your command. Your command or shell script never sees whether or not you have a variable as a parameter.
$ set -x
set -x
$ foo=bar
+ foo=bar
$ echo "$foo"
+ echo bar
bar
$ set +x
The set -x turns on debugging mode in the shell. It shows you what a command actually executes. For example, I set foo=bar and then do echo $foo. My echo command doesn't see $foo. Instead, before echo executes, it interpolates $foo with bar. All echo sees at this point is that it's suppose to take bar as its argument (not $foo).
This is awesomely powerful. It means that your program doesn't have to sit there and interpret the command line. If you typed echo *.txt, echo doesn't have to expand *.txt because the shell has already done the dirty work.
For example, here's a test shell script:
#! /bin/sh
if [[ $1 = "*" ]]
then
echo "The first argument was '*'"
else
"I was passed in $# parameters"
fi
Now, I'll run my shell script:
$ test.sh *
I was passed in 24 parameters
What? Wasn't the first parameter of my script a *? No. The shell grabbed * and expanded it to be all of the files and directories in my directory. My shell script never saw the *. However, I can do this:
$ test.sh '*'
The first argument was '*'
The single quotes tell the shell not to interpolate anything. (Double quotes prevent globbing, but still allow for environment variable expansion).
This if I wanted to see if my first parameter is a variable, I have to pass it in single quotes:
$ test.sh '$foo'
And, I can do this as a test:
if [[ $1 != ${1#$} ]]
then
echo "The first parameter is the variable '$1'"
fi
The ${1#$} looks a bit strange, but it's just ${var#pattern}. This removes pattern from the left most side of $var. I am taking $1 and removing the $ if it exists. This gets expanded in the shell as:
if [[ $foo != foo ]]
which is true.
So, several things:
First, you've got to stop the shell from interpolating your variable. That means you have to use single quotes around the name.
You have to use pattern matching to verify that the first parameter starts with a $.
Once you do that, you should be able to use your variable with ${$1} in your script.

Bash indirect variable assignment inside a function

I have a script where the user input needs to be evaluated several times, the solution im working on is to put the evaluation bits into a function, and simply call the function every time i need to evaluate the input.
The problem is though that when im trying to update the $1 variable (that referes to the first variable parameter of the function) I get the error message "$VARIABLE command not found".
Here is the code:
function input_handler() {
if is_integer $1; then
selid="$1 -1"
if [[ "$1" -le "0" ]]; then
echo "Please use a simple positive number!"
else
if [[ "$1" -le "${#array[*]}" ]]; then
eval $1="${array[selid]}"
echo "Ok, moving on..."
else
echo "That number seems too large, try again?"
fi
fi
else
if [ -e $2/$1 ]; then
echo "Ok, moving on..."
else
echo "That item is not on the list, try again!"
fi
fi
}
And this command:
input_handler $doctype $docpath
Gives this output:
5
./test: line 38: 5=sun: command not found
Ok, moving on...
Now this is almost correct, but what im after is doctype=sun, not 5=sun, in other words I need the $1 variable name not its value. Changing the line eval $1="${array[selid]}" to eval doctype="${array[selid]}" fixes this particular instance. But this does not fix my problem as I need to run this function on different variables with different names.
Maybe not fully understand what you want achieve, but check the next example:
weirdfunc () {
echo " weirdfunc: variable name is: $1"
echo " weirdfunc: variable value is: ${!1}"
eval "$1=$(( ${!1} + 1))" #assign
}
myvar="5"
echo "the value of myvar before: $myvar"
weirdfunc myvar #call with the NAME not with the value, so NOT weridfunc $myvar
echo "the value of myvar after: $myvar"
In short - when you want to do anything with the variable NAME in an called function, you should pass the NAME of the variable and NOT his value. So call the function
somefunc NAME
instead of
somefunc $NAME
and use the above constructs to get the name and value inside the function.
You can't update the value of $1 with a traditional assignment, but you can update the positional parameters with the set builtin.
$ f() { echo "$#"; set -- a b c; echo "$#"; echo $2; }
$ f 1 2 3
1 2 3
a b c
b
Just keep in mind this will wipe out all the positional parameters you don't re-set each time, so you'll need to set $2 if you want to keep it around.
Your best bet is probably to assign the values in the positional parameters to names and just use names from then on.
If you protect the variable name, Bash will evaluate and assign to $1 instead of try to execute $1=value.
eval "$1"=${array[selid]}
Positional parameters are read-only. So what you want to do is not possible. You should do something like
foo=$1
and then work with $foo instead of $1

how do i create a variable from the output of another in bash

Here's an example script that doesn't work the way I expect:
#!/bin/bash
for dynamic in a b c; do
myvar=$dynamic
export $myvar="hi"
echo $(eval "$myvar")
echo $dynamic
done
I want the output would be:
hi
a
hi
b
hi
c
Any ideas? I'm willing to stray away from this method, but I definitely want to be able to create a variable named from the output of an algorithm. In this case it's just a for loop.
eval has a tendency to cause bugs, so avoid it whenever possible; in this case it's much cleaner to use indirect expansion with ${!metavariable}:
#!/bin/bash
for dynamic in a b c; do
myvar=$dynamic
export $myvar="hi"
echo ${!myvar}
echo $dynamic
done
The following is the fix for your program. There are two things you got wrong:
The first is you don't need '$' when declaring variables.
The second is that calling eval will treat the content of myvar as a shell script. However you don't have "hi" defined anywhere as a command.
for dynamic in a b c; do
myvar=$dynamic
- export $myvar="hi"
+ export myvar="hi"
- echo $(eval "$myvar")
+ echo "$myvar"
echo $dynamic
done
It's not entirely clear that this is what you're looking for, but I think you want something like:
#!/bin/sh
a=A
b=B
c=C
for i in a b c; do
eval $i=value_$i
eval echo \$$i
done
echo $a # Prints "value_a"

Resources