1

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!

noes
  • 49
  • 1
  • 7

3 Answers3

5

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.

Tom Fenech
  • 72,334
  • 12
  • 107
  • 141
  • 1
    The solution with the array works perfectly. Thank you very much! – noes Mar 01 '16 at 14:13
  • Agreed, arrays are the way to go here ([some reasons why eval is problematic for the curious](http://stackoverflow.com/q/17529220/3149036)) – tavnab Mar 01 '16 at 21:30
2

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
Community
  • 1
  • 1
tavnab
  • 2,594
  • 1
  • 19
  • 26
0

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.

Benjamin W.
  • 46,058
  • 19
  • 106
  • 116