0

I declared var0, var1, var2, var3 with a for loop. How do I echo the vars inside that for loop? Here's the code.

#!/bin/bash

for i in {0..3}
do
  export var$i=$i
done;

There i defined var0, var1, var2, var3.. how do i access them in a loop?

i tried the following

for i in {0..3}
do
  echo $var$i
  tmpvar=var$i
  echo $tmpvar
done

but none gave me the values of var0,var1... first echo just printed '0,1,2,3', second echo printed 'var0,var1,var2,var3' what do i do? I want the values..

J. Doe
  • 3

2 Answers2

5

Given,

var0=a
var1=b
var2=c

with ksh93 or bash 4.3 or newer, you can use a nameref to point at the individual variables:

for i in 0 1 2; do
    typeset -n p="var$i"
    echo "$p"
done

But unless you explicitly need the variables as separate scalars (for example because you passed them through the environment), and are not using a strictly standard shell, but Bash or ksh, you should use an array instead:

#!/bin/bash
a=(a b c)
for i in 0 1 2; do
    echo "${a[i]}"
done

Or ignoring the indexes:

for x in "${a[@]}"; do
    echo "$x"
done

(Zsh, of course, has arrays too, it just starts indexing from 1 by default so the first one doesn't work directly.)

ilkkachu
  • 138,973
  • No reproach here, answer is correct, given OP specified the bash tag, so arrays are legit. However (and it's a mere opinion) I would try to stay away from the array cookie-jar for portability reasons. So the last incarnation of @mosvy's answer (incorporating Stéphane's comment) is definitely a go if you work with many different *nix-centric OSes. – Cbhihe Sep 17 '18 at 07:34
  • @Cbhihe, yeah, it depends of course. Arrays are seriously useful if one needs to deal with, well, array-like data in the shell. I would be tempted to look into compiling Bash or ksh if I had to do that on a system with just plain sh... – ilkkachu Sep 17 '18 at 08:34
  • Note that it's not just zsh that has arrays starting at 1. It's all shells but ksh and bash (including Bourne/POSIX ("$@"), csh, yash, fish, rc, es...). In zsh, you can set the ksharrays option if you want then to start at 0. – Stéphane Chazelas Sep 17 '18 at 09:09
4

You can use eval for that:

$ var1='< 1 >'; var2='< 2 >'; var3='< 3 >'
$ for i in 1 2 3; do eval echo \$var$i; done
$ for i in 1 2 3; do eval "echo \$var$i"; done
$ for i in 1 2 3; do eval 'echo $var'$i; done
$ for i in 1 2 3; do eval "v=\$var$i"; echo "$v"; done

Take care with the quoting and escaping. If the values contain whitespace or glob characters, the first three will split the values on whitespace, and then expand any filenames that match the glob. This may or may not be what you want. For example:

$ var1='<  x  >' var2='< * >'
$ for i in 1 2; do eval echo \$var$i; done
< x >
< [list of filenames in current directory] >

(Note that the double spaces around x were collapsed to single spaces since echo received <, x and > as separate arguments.)

To work around that, you need to make sure the variable expansion is quoted within the eval, e.g.:

$ for i in 1 2; do eval echo \"\$var"$i"\"; done
<  x  >
< * >

(quote the $i too, as above, unless you know IFS doesn't contain any digits.)

ilkkachu
  • 138,973
  • 2
    Leaving those variables unquoted is invoking the split+glob operator which you don't want here. See for instance Security implications of forgetting to quote a variable in bash/POSIX shells. Try with IFS=123 or var1='*' for instance. – Stéphane Chazelas Sep 16 '18 at 22:02
  • The IFS splitting and globbing is not some kind of bug, but useful and well-documented behavior, maybe I really want it?. These examples are meant to illustrate how "eval" works, and the quote clutter stays in the way and gives the false impression that it guards against actually executing commands from var1, etc. Anyways, I made the last example split+glob immune. –  Sep 16 '18 at 22:48
  • 3
    @mosvy, It's not about preventing the command execution, it's about keeping the values of the variables intact. Also, it's not about what you want, it's about what the people reading this answer want. And just out experience from reading the questions here, most of the time they don't want to split'n'glob when using variables. Here, you chose the variables very carefully so that echo will output them seemingly intact even after splitting (with the default IFS) and globbing. But just something like <␣␣1␣␣> or <␣*␣> would not stay as-is. – ilkkachu Sep 17 '18 at 08:26
  • @ilkkachu, the $i is still unquoted in your edit so subject to split+glob (which means that code can't be used in contexts where $IFS may have been modified), see my original edit (which mosvy reverted). – Stéphane Chazelas Sep 17 '18 at 08:57
  • This question was not about the split+glob; please notice that the use of eval makes no difference (you can do the same with a=*; echo $a, big f* deal), so it's just a distraction, and gives the impression that "eval" is especially tricky in this regard. Simply linking 'quoting to escaping' from the last sentence to the Stéphane Chazelas' parade of horribles instead of repeating part of the stuff here would've been much nicer, but I'm not going to engage in an edit war. As to why splitting with IFS is wanted: it's the only way to do lists and substitutions portably. –  Sep 17 '18 at 08:59
  • 2
    Leaving a parameter unquoted when you don't want split+glob is bad coding practice. As far as possible we should avoid bad coding practice in code posted in answers. – Stéphane Chazelas Sep 17 '18 at 09:01
  • @mosvy, the same caveats would be pointed out even if there were no evals in sight. – ilkkachu Sep 17 '18 at 09:03
  • @ilkkachu There's a lot to point out everywhere, but stuffing any answer with unrelated caveats and pet-peeves is muddling the point; way too many answers here are like that, there was no need to turn my answer into yet another one. –  Sep 17 '18 at 09:15