Convert a string into a variable in UNIX - shell

I have the following code
let a=1
let b=a
echo $b
When i echo b, i want it to refer it's value (which is 'a'), and display the value of a( which is '1')
Can this be achieved?

First, I would argue you don't really want to do this. But instead of convincing you, here's how to do it:
$ let a=1
$ b=a
$ echo $b
a
$ eval "echo \$$b"
1
note that you can't use "let" for the second assignment since you want to access the right-hand-side as a string later.

I think you want let b=$a

If your shell is bash, then what you wrote actually sets b to "1" (not "a") because the $ is optional in arithmetic expressions (which you force with the use of let).
Without using let, this is the syntax you're looking for (assuming bash): a=1; b=a; echo $b ${!b} which outputs a 1

Related

bash expanding value of a variable to use in another variable

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

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"

What is the problem with my code for multiplying two numbers in a different way using Bash?

This is my first time using Bash, so bear with me. I'm using Git Bash in Windows for a college project, trying to rewrite some C code that provides an alternate way of multiplying two numbers "a" and "b" to produce "c". This is what I came up with:
#!/bin/bash
declare -i a
declare -i b
declare -i c=0
declare -i i=0 # not sure if i have to initialize this as 0?
echo "Please enter a number: "
read a
echo "Please enter a number: "
read b
for i in {1..b}
do
let "c += a"
done
echo "$a x $b = $c"
I think part of the problem is in the for loop, that it only executes once. This is my first time using Bash, and if anyone could find the fault in my knowledge, that would be all I need.
There are problems with your loop:
You can't use {1..b}. Even if you had {1..$b} it wouldn't work because you would need an eval. It's easiest to use the seq command instead.
Your let syntax is incorrect.
Try this:
for i in $(seq 1 $b)
do
let c+=$a
done
Also, it's not necessary to declare or initialise i.
for i in {1..b}
won't work, because 'b' isn't interpreted as a variable but a character to iterate to.
For instance {a..c} expands to a b c.
To make the brace expansion work:
for i in $(eval echo "{1..$b}")
The let "c += a" won't work either.
let c+=$a might work, but I like ((c+=a)) better.
Another way is this:
for ((i = 1; i <= b; i++))
do
((c += a))
done
(might need to put #!/bin/bash at the top of your script, because sh does less than bash.)
Of course, bash already has multiplication, but I guess you knew that ...
If the absence of "seq" is your issue, you can replace it with something more portable, like
c=0
# Print an endless sequence of lines
yes |
# Only take the first $b lines
head -n "$b" |
# Add line number as prefix for each line
nl |
# Read the numbers into i, and the rest of the line into a dummy variable
while read i dummy; do
# Update the value of c: add line number
c=`expr "$c" + "$i"`
echo "$c"
done |
# Read the last number from the while loop
tail -n 1
This should be portable to any Bourne-compatible shell. The while ... echo ... done | tail -n 1 trick is necessary only if the value of c is not exported outside the while loop, as is the case in some, but not all, Bource shells.
You can implement a seq replacement with a Perl one-liner, but then you might as well write all of this in Perl (or awk, or Python, or what have you).

Variable substitution in a for-loop using {$var}

I'm very new to bash scripting and I'm trying to practice by making this little script that simply asks for a range of numbers. I would enter ex. 5..20 and it should print the range, however - it just echo's back whatever I enter ("5..20" in this example) and does not expand the variable. Can someone tell me what I'm doing wrong?
Script:
echo -n "Enter range of number to display using 0..10 format: "
read range
function func_printrage
{
for n in {$range}; do
echo $n
done
}
func_printrange
Brace expansion in bash does not expand parameters (unlike zsh)
You can get around this through the use of eval and command substitution $()
eval is evil because you need to sanitize your input otherwise people can enter ranges like rm -rf /; and eval will run that
Don't use the function keyword, it is not POSIX and has been deprecated
use read's -p flag instead of echo
However, for learning purposes, this is how you would do it:
read -p "Enter range of number to display using 0..10 format: " range
func_printrange()
{
for n in $(eval echo {$range}); do
echo $n
done
}
func_printrange
Note: In this case the use of eval is OK because you are only echo'ing the range
One way is to use eval,
crude example,
for i in $(eval echo {0..$range}); do echo $i; done
the other way is to use bash's C style for loop
for((i=1;i<=20;i++))
do
...
done
And the last one is more faster than first (for example if you have $range > 1 000 000)
One way to get around the lack of expansion, and skip the issues with eval is to use command substitution and seq.
Reworked function (also avoids globals):
function func_print_range
{
for n in $(seq $1 $2); do
echo $n
done
}
func_print_range $start $end
Use ${} for variable expansion. In your case, it would be ${range}. You left off the $ in ${}, which is used for variable expansion and substitution.

Arithmetic comparison in a shell

i want to compare two number values in a shell script (sh) but it doesn`t work:
#!/bin/sh
let a=30
let b=100
let x=$a-$b
echo $a $b $x
[ $a < $b ] && { echo ok; }
That outputs:
30 100 -70
./x: line 6: 100: No such file or directory
I believe that should be -lt (which stands for less than) rather than "<". "<" is for string comparisons.
Edit: Actually looking at this now it seems clear what the problem is. The "<" character does file redirection so that's what the shell is trying to do. You can escape that character by doing \< instead but as originally stated that will do string comparison rather than numeric comparison.
Replace < with -lt
Also, lose the "let." This isn't Basic. Bill Gates and Steve Ballmer (Developers!) have nothing to do with this universe.
"let" is perfectly fine in shell, and has nothing to do with M$ basic or what.!
#OP , you can use bc to compare numbers, especially if you are also comparing floats. See here for similar example
#!/bin/sh
let a=30
let b=100
let x=$a-$b
echo $a $b $x
if(($a < $b)) then
echo ok
fi

Resources