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
  • 3
    it may be more sensible to use an array rather than trying to use variables as variable names – thrig Sep 16 '18 at 17:01
  • 5
    I believe it is called *"Indirect variable reference"*. Possible duplicate of [Use a variable reference "inside" another variable](https://unix.stackexchange.com/q/41406/56041) –  Sep 16 '18 at 21:16

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
  • 133,243
  • 15
  • 236
  • 397
  • 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
  • 133,243
  • 15
  • 236
  • 397
  • 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](//unix.stackexchange.com/q/171346). 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 `eval`s 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