How to use variables in a bash for loop

LinuxBashFor Loop

Linux Problem Overview


How does one use a variable in a bash for loop? If I just use a standard for loop, it does what I expect

for i in {0..3}
do
   echo "do some stuff $i"
done

This works fine. It loops thru 4 times, 0 to 3 inclusive, printing my message and putting the count at the end.

do some stuff 0
do some stuff 1
do some stuff 2
do some stuff 3

When I try the same thing with the following for loop, it seems to equal a string, which is not what i want.

length=3
for i in {0..$length}
do
   echo "do something right $i"
done

output:

do something right {0..3}

I've tried

for i in {0.."$length"} and for i in {0..${length}} (both output was {0..3})

and

for i in {0..'$length'} (output was {0..$length})

and they both don't do what I need. Hopefully someone can help me. Thanks in advance for any bash expert's help with for loops.

Linux Solutions


Solution 1 - Linux

One way is using eval:

for i in $( eval echo {0..$length} )
do
       echo "do something right $i"
done

Note what happens when you set length=;ls or length=; rm * (don't try the latter though).

safely, using seq:

for i in $( seq 0 $length )
do
       echo "do something right $i"
done

or you can use the c-style for loop, which is also safe:

for (( i = 0; i <= $length; i++ )) 
do 
       echo "do something right $i"
done

Solution 2 - Linux

In bash, brace expansion is the first step attempted so, at that point, $length will not have been substituted.

The manpage for bash states clearly:

> A sequence expression takes the form {x..y[..incr]}, where x and y are either integers or single characters ...

There are a number of possibilities, such as using:

pax> for i in $(seq 0 $length) ; do echo $i ; done
0
1
2
3

though that may give you a large command line if length is massive.

Another alternative is to use the C-like syntax:

pax> for (( i = 0; i <= $length; i++ )) ; do echo $i; done
0
1
2
3

It's also possible to omit $ sign in double parentheses to refer a variable:

ubuntu@ip-172-31-28-53:~/playground$ length=3;
ubuntu@ip-172-31-28-53:~/playground$ for ((i=0;i<=length;i++));do echo $i;done
0
1
2
3

Solution 3 - Linux

Brace subtitutions are performed before any other, so you need to use eval or a third-party tool like seq.

Example for eval:

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

This information can actually be found in man bash:

> A sequence expression takes the form {x..y[..incr]}, where x and y are either > integers or single characters, and incr, an optional increment, is an integer. > [...] > > Brace expansion is performed before any other expansions, and any characters special > to other expansions are preserved in the result. It is strictly textual. > Bash does not apply any syntactic interpretation to the context of the expansion > or the text between the braces.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionClassifiedView Question on Stackoverflow
Solution 1 - LinuxperrealView Answer on Stackoverflow
Solution 2 - LinuxpaxdiabloView Answer on Stackoverflow
Solution 3 - LinuxnemoView Answer on Stackoverflow