277

I have gotten the following to work:

for i in {2..10}
do
    echo "output: $i"
done

It produces a bunch of lines of output: 2, output: 3, so on.

However, trying to run the following:

max=10
for i in {2..$max}
do
    echo "$i"
done

produces the following:

output: {2..10}

How can I get the compiler to realize it should treat $max as the other end of the array, and not part of a string?

6
  • 2
    what system and shell are you using? What kind of goofy system has sh or bash, but doesn't have seq, a coreutil? Commented Sep 18, 2009 at 16:08
  • 13
    FreeBSD doesn't. Commented Sep 18, 2009 at 16:12
  • Small style nit: I usually see the do and then keywords on the same line as for and if, respectively. E.g., for i in {2..10}; do Commented Sep 18, 2009 at 16:33
  • possible duplicate of Is it possible to use a variable in for syntax in bash? Commented Aug 1, 2013 at 15:00
  • 1
    FreeBSD, at least 10, does have /usr/bin/seq. Commented May 10, 2015 at 18:46

11 Answers 11

327

Brace expansion, {x..y} is performed before other expansions, so you cannot use that for variable length sequences.

Instead, use the seq 2 $max method as user mob stated.

So, for your example it would be:

max=10
for i in `seq 2 $max`
do
    echo "$i"
done
Sign up to request clarification or add additional context in comments.

3 Comments

There is no good reason to use an external command such as seq to count and increment numbers in the for loop, hence it is recommend that you avoid using seq. This command is left for compatibility with old bash. The built-in commands are fast enough. for (( EXP1; EXP2; EXP3 )) ...
@miller the for (( ... )) syntax isn't POSIX and that means for example that it won't work out of the box on things like Alpine Linux. seq does.
@Wernight Not just Alpine Linux; #!/bin/sh is Dash in Debian/Ubuntu.
102

Try the arithmetic-expression version of for:

max=10
for (( i=2; i <= $max; ++i ))
do
    echo "$i"
done

This is available in most versions of bash, and should be Bourne shell (sh) compatible also.

2 Comments

@Flow: Hm, I just tried it on a couple of systems (Linux and BSD based) with #!/bin/sh and it worked fine. Invoked under bash and specifically under /bin/sh, still worked. Maybe the version of sh matters? Are you on some old Unix?
Very few systems have a dedicated sh, instead making it a link to other another shell. Ideally, such a shell invoked as sh would only support those features in the POSIX standard, but by default let some of their extra features through. The C-style for-loop is not a POSIX feature, but may be in sh mode by the actual shell.
40

Step the loop manually:

i=0
max=10
while [ $i -lt $max ]
do
    echo "output: $i"
    true $(( i++ ))
done

If you don’t have to be totally POSIX, you can use the arithmetic for loop:

max=10
for (( i=0; i < max; i++ )); do echo "output: $i"; done

Or use jot(1) on BSD systems:

for i in $( jot 0 10 ); do echo "output: $i"; done

3 Comments

true $(( i++ )) doesn't work in all cases, so most portable would be true $((i=i+1)).
semi colon should not come in "for (( i=0; i < max; i++ ));"
jot 0 10 might be an infinite loop starting at 10. To get the same effect as max=10; for (( i=0; i < max; i++ )); do echo "output: $i"; done I think the syntax should be jot -w 'output: %d' 10 0 (directly print out sans loop) or just for i in $( jot 10 0 ); do ….. for the looped variant
21

If the seq command available on your system:

for i in `seq 2 $max`
do
  echo "output: $i"
done

If not, then use poor man's seq with perl:

seq=`perl -e "\$,=' ';print 2..$max"`
for i in $seq
do
  echo "output: $i"
done

Watch those quote marks.

Comments

19

We can iterate loop like as C programming.

#!/bin/bash
for ((i=1; i<=20; i=i+1))
do 
      echo $i
done

Comments

10

There's more than one way to do it.

max=10
for i in `eval "echo {2..$max}"`
do
    echo "$i"
done

2 Comments

A security risk, since eval will evaluate anything you set max to. Consider max="2}; echo ha; #", then replace echo ha with something more destructive.
(S)he set max to 10. No risk.
5

This is a way:
Bash:

max=10
for i in $(bash -c "echo {2..${max}}"); do echo $i; done

The above Bash way will work for ksh and zsh too, when bash -c is replaced with ksh -c or zsh -c respectively.

Note: for i in {2..${max}}; do echo $i; done works in zsh and ksh.

Comments

3

Well, as I didn't have the seq command installed on my system (Mac OS X v10.6.1 (Snow Leopard)), I ended up using a while loop instead:

max=5
i=1

while [ $max -gt $i ]
do
    (stuff)
done

*Shrugs* Whatever works.

1 Comment

seq is relatively new. I only found out about it a few months ago. But you can use a 'for' loop!! The disadvantage of a 'while' is that you have to remember to increment the counter somewhere inside the loop, or else loop downwards.
2

These all do {1..8} and should all be POSIX. They also will not break if you put a conditional continue in the loop. The canonical way:

f=
while [ $((f+=1)) -le 8 ]
do
  echo $f
done

Another way:

g=
while
  g=${g}1
  [ ${#g} -le 8 ]
do
  echo ${#g}
done

and another:

set --
while
  set $* .
  [ ${#} -le 8 ]
do
  echo ${#}
done

1 Comment

Wonderful, I'm looking for a solution for busybox's ash, and your answer is perfect
2

Here it worked on Mac OS X.

It includes the example of a BSD date, how to increment and decrement the date also:

for ((i=28; i>=6 ; i--));
do
    dat=`date -v-${i}d -j "+%Y%m%d"` 
    echo $dat
done

Comments

1

Use:

max=10
for i in `eval echo {2..$max}`
do
    echo $i
done

You need the explicit 'eval' call to reevaluate the {} after variable substitution.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.