In bash shell script, I want to create series of variables and assign string values to them
a=aaa
eval $a="a b c"
turns out :
b not found...
Why the right value cannot contain space?
It's because eval $a="a b c" is expanded to aaa=a b c
Solutions:
eval "$a=\"a b c\""
# or better
printf "-v$a" "a b c"
You can reach for declare when you want to use eval for variable assignment.
$ declare "$a=a b c"
$ echo $aaa
a b c
Related
I would like to write a for in loop in which the possible values are taken from another variable. The part that I can't figure out is how to include values that need brace expansion. For example:
TEXT="a b c d{a..c} e f"
for VAR in $TEXT; do echo $VAR; done
What i get is
a
b
c
d{a..c}
e
f
But what I want is
a
b
c
da
db
dc
e
f
Does anyone know how I can get this to work?
Thank you very much!
I fear that you may have to resort to using eval in this case...However, I'd suggest that you avoided using a loop:
eval "printf '%s\n' $TEXT"
Using eval means that the braces will be expanded, producing your desired outcome.
You can see what happens using set -x:
$ set -x
$ eval "printf '%s\n' $TEXT"
+ eval 'printf '\''%s\n'\'' a b c d{a..c} e f'
++ printf '%s\n' a b c da db dc e f
a
b
c
da
db
dc
e
f
Note that the expansion occurs before the list is passed as arguments to printf.
If you get to define $TEXT yourself and want to loop through each of its expanded elements, I'd suggest using an array instead:
text=( a b c d{a..c} e f )
for var in "${text[#]}"; do
# whatever with "$var"
done
This is the preferred solution as it doesn't involve using eval.
EDIT
While there's a time & place for eval, it's not the safest approach for the general case. See Why should eval be avoided in Bash, and what should I use instead? for some reasons why.
Use eval:
TEXT="a b c d{a..c} e f"
for VAR in $TEXT; do eval echo $VAR; done
Output:
a
b
c
da db dc
e
f
If you want to loop over every element, you'll have to nest your loops:
for VAR in $TEXT; do
for VAR2 in $(eval echo $VAR); do
echo $VAR2
done
done
Output:
a
b
c
da
db
dc
e
f
Or use the eval on TEXT rather than each element:
TEXT="$(eval echo "a b c d{a..c} e f")"
for VAR in $TEXT; do echo $VAR; done
Output:
a
b
c
da
db
dc
e
f
Without eval, but with command substitution:
$ text="$(echo a b c d{a..c} e f)"
$ echo "$text"
a b c da db dc e f
And, as mentioned in Tom's answer, an array is probably the better way to go here.
In bash, I can loop over all arguments, $#.
Is there a way to get the index of the current argument? (So that I can refer to the next one or the previous one.)
You can loop over the argument numbers, and use indirect expansion (${!argnum})to get the arguments from that:
for ((i=1; i<=$#; i++)); do
next=$((i+1))
prev=$((i-1))
echo "Arg #$i='${!i}', prev='${!prev}', next='${!next}'"
done
Note that $0 (the "previous" argument to $1) will be something like "-bash", while the "next" argument after the last will come out blank.
Not exactly as you specify, but you can iterate over the arguments a number of different ways.
For example:
while test $# -gt 0
do
echo $1
shift
done
Pretty simple to copy the positional params to an array:
$ set -- a b c d e # set some positional parameters
$ args=("$#") # copy them into an array
$ echo ${args[1]} # as we see, it's zero-based indexing
b
And, iterating:
$ for ((i=0; i<${#args[#]}; i++)); do
echo "$i ${args[i]} ${args[i-1]} ${args[i+1]}"
done
0 a e b
1 b a c
2 c b d
3 d c e
4 e d
I have a bash script called foo with variable number of arguments, with the first one being a required one, i.e.:
foo a1 b2 b3 b4 ...
I understand that in bash $1 will get me argument a1, but is there a way to get all the rest of the arguments? $# or $* seem to get me all the arguments including a1.
Slice the $# array.
echo "${#:2}"
You can use shift command for that. That will remove $1 and you can access the rest of arguments starting with $1.
#!/bin/sh
echo $*
shift
echo $*
shift will shift all the parameters, running the previous example would give:
$ test_shift.sh a b c d e
a b c d e
b c d e
./foo.sh 1 2 3 4
#!/bin/bash
echo $1;
echo $2;
echo $3;
echo $4;
Will output:
1
2
3
4
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
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"