Using a variable in brace expansion range fed to a for loop
Asked Answered
M

5

49

Here is myscript.sh

#!/bin/bash
for i in {1..$1};
do
    echo $1 $i;
done

If I run myscript.sh 3 the output is

3 {1..3}

instead of

3 1
3 2
3 3

Clearly $3 contains the right value, so why doesn't for i in {1..$1} behave the same as if I had written for i in {1..3} directly?

Mingmingche answered 28/3, 2012 at 15:41 Comment(2)
Your example script will work in both ksh93 and zsh.Phio
Minor thing: You wrote $3 ("Clearly $3 contains ...") but are using $1 in your script. Why not just remove the $1? It doesn't have that much relevance except to prove that variable expansion works.Diachronic
S
70

You should use a C-style for loop to accomplish this:

for ((i=1; i<=$1; i++)); do
   echo $i
done

This avoids external commands and nasty eval statements.

Smiga answered 28/3, 2012 at 21:56 Comment(5)
Not working for me test.sh: line 1: ((: i<=: syntax error: operand expected (error token is "<=")Goda
@Goda are you using bash? Is your shebang #!/bin/bash or #!/bin/sh?Smiga
@jordann, file contains only these 3 lines. I'm using GNU bash, version 4.3.11(1)-release (x86_64-pc-linux-gnu). I run script bash test.shGoda
@Goda you need to run it like this: bash test.sh 12. The $1 is a positional arg.Smiga
Yes, I haven't noticed. Sorrry:)Goda
U
27

Because brace expansion occurs before expansion of variables. http://www.gnu.org/software/bash/manual/bashref.html#Brace-Expansion.

If you want to use braces, you could so something grim like this:

for i in `eval echo {1..$1}`;
do
    echo $1 $i;
done

Summary: Bash is vile.

Unfriended answered 28/3, 2012 at 15:44 Comment(5)
Heh, +1 for the answer, -0.5 for the vile.Ewart
@glennjackman: Wow, you're saying that Bash's myriad expansion and escaping rules aren't vile?Unfriended
I feel dirty for saying +1 on an answer involving eval. Re: vile: bash isn't vile, but it's a bit like drinking grain alcohol straight. Some people seem to like it but it's hard not to choke at first and the more you do it the less you're bothered by it.Tremayne
@Sorpigal: It must just be me then. I use it pretty much every day, and it still bothers me... (but then I'm not good with straight alchohol either)Unfriended
This is yet working and solving the issue. Thank you!Merciful
H
16

You can use seq command:

for i in `seq 1 $1`

Or you can use the C-style for...loop:

for((i=1;i<=$1;i++))
Hypochlorite answered 28/3, 2012 at 15:45 Comment(1)
A C-style for loop is preferred over the external seq command.Smiga
M
2

Here is a way to expand variables inside braces without eval:

end=3
declare -a 'range=({'"1..$end"'})'

We now have a nice array of numbers:

for i in ${range[@]};do echo $i;done
1
2
3
Metzger answered 21/1, 2015 at 12:46 Comment(0)
C
1

I know you've mentioned bash in the heading, but I would add that 'for i in {$1..$2}' works as intended in zsh. If your system has zsh installed, you can just change your shebang to zsh.

Using zsh with the example 'for i in {$1..$2}' also has the added benefit that $1 can be less than $2 and it still works, something that would require quite a bit of messing about if you wanted that kind of flexibility with a C-style for loop.

Closure answered 4/2, 2014 at 6:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.