258

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?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
eykanal
  • 26,437
  • 19
  • 82
  • 113
  • 2
    what system and shell are you using? What kind of goofy system has sh or bash, but doesn't have seq, a coreutil? – whatsisname Sep 18 '09 at 16:08
  • 12
    FreeBSD doesn't. – Nietzche-jou Sep 18 '09 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` – a paid nerd Sep 18 '09 at 16:33
  • possible duplicate of [Is it possible to use a variable in for syntax in bash?](http://stackoverflow.com/questions/17787681/is-it-possible-to-use-a-variable-in-for-syntax-in-bash) – Barmar Aug 01 '13 at 15:00
  • FreeBSD, at least 10, does have /usr/bin/seq. – jrm May 10 '15 at 18:46
  • @whatsisname, `seq` is not POSIX-standardized. Even if it exists, it's not guaranteed to have any particular behavior. There's a reason none of the patterns discussed in http://wooledge.org/~greybot/meta/counting (the relevant entry in the freenode #bash channel's factoid database) depend on it. – Charles Duffy Sep 18 '20 at 16:35

11 Answers11

300

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
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
whatsisname
  • 5,872
  • 2
  • 20
  • 27
  • 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 Jul 25 '16 at 09:27
  • 16
    @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 Sep 30 '16 at 22:21
  • @Wernight Not just Alpine Linux; `#!/bin/sh` is Dash in Debian/Ubuntu. – Franklin Yu Mar 12 '19 at 20:50
90

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.

system PAUSE
  • 37,082
  • 20
  • 62
  • 59
  • 1
    @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? – system PAUSE Mar 26 '12 at 21:29
  • 8
    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. – chepner Sep 12 '13 at 19:41
35

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 &lt 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
Nietzche-jou
  • 14,415
  • 4
  • 34
  • 45
18

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.

mob
  • 117,087
  • 18
  • 149
  • 283
16

We can iterate loop like as C programming.

#!/bin/bash
for ((i=1; i<=20; i=i+1))
do 
      echo $i
done
rashedcs
  • 3,588
  • 2
  • 39
  • 40
9

There's more than one way to do it.

max=10
for i in `eval "echo {2..$max}"`
do
    echo "$i"
done
ephemient
  • 198,619
  • 38
  • 280
  • 391
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.

Jahid
  • 21,542
  • 10
  • 90
  • 108
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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
eykanal
  • 26,437
  • 19
  • 82
  • 113
  • 2
    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. – system PAUSE Sep 18 '09 at 16:23
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
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
minhas23
  • 9,291
  • 3
  • 58
  • 40
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

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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Chris Dodd
  • 2,920
  • 15
  • 10