320

I'm looking for the basic loop like:

for (int i = 0; i < MAX; i++) {
    doSomething(i);
}

but for bash.

1

9 Answers 9

345

From this site:

for i in $(seq 1 10);
do
    echo $i
done
Sign up to request clarification or add additional context in comments.

3 Comments

It's worth a mention that the range specified here is inclusive. By that, I mean you will see the entire range (1 to 10) printed to the console.
Note that trailing ; on the for line is not actually necessary when there is a newline before the do statement. However, the ; syntax can be used to concatenate do onto the for line.
when I used up-arrow in my bash today, I discovered that the whole loop had been condensed to a single line with ; replacing the newlines. That was unanticipated but welcome.
168
for ((i = 0 ; i < max ; i++ )); do echo "$i"; done

3 Comments

if we have a variable like ArrayLength=${#array[@]} how do we use it in here instead of max?
I prefer this solution since it does not use a seq so it does not generate a weird string containing a sequence of all the numbers, but it just loops efficiently over an integer.
It's a better choice. Because when max is an overwhelming value, it doesn't allocate so much memory to hold all of items in place. It just acts like how C does.
133

Bash 3.0+ (released 2004-07-27) can use this syntax:

for i in {1..10} ; do ... ; done

...which avoids spawning an external program to expand the sequence (such as seq 1 10).

Of course, this has the same problem as the for(()) solution, being tied to Bash and even a particular version (if this matters to you).

1 Comment

Might not be ideal for long sequences(millions) because the sequence is actually expanded. Better to use for ((i = 0 ; i < max ; i++ )) as in another answer.
96

The Bash for consists of a variable (the iterator) and a list of words over which the iterator will, well, iterate.

So, if you have a limited list of words, just put them in the following syntax:

for w in word1 word2 word3
do
  doSomething($w)
done

Probably you want to iterate over some numbers, so you can use the seq command to generate a list of numbers for you: (from 1 to 100 for example)

seq 1 100

and use it in the for loop:

for n in $(seq 1 100)
do
  doSomething($n)
done

Note the $(...) syntax. It's a Bash behaviour, and it allows you to pass the output from one command (in our case from seq) to another (the for).

This is really useful when you have to iterate over all directories in some path, for example:

for d in $(find $somepath -type d)
do
  doSomething($d)
done

The possibilities are infinite to generate the lists.

2 Comments

Good answer, but you might want to include the for ((i=0; i<MAX; i++)); do doSomething($i); done variant as well. I think this generally preferred to the for i in $(seq 0 MAX) variant as the latter will first generate all numbers from 0 to MAX before actually executing the loop.
I am working on a find for loop, but it does not like the $ in front of the parenthesis. for i in $(find -type f -name "*.fastq.gz" | while read F; do basename $F | rev | cut -c 55- | rev; done | sort | uniq) bash: syntax error near unexpected token `for'
24

Try the Bash built-in help:

help for

Output

for: for NAME [in WORDS ... ;] do COMMANDS; done
    The `for' loop executes a sequence of commands for each member in a
    list of items.  If `in WORDS ...;' is not present, then `in "$@"' is
    assumed.  For each element in WORDS, NAME is set to that element, and
    the COMMANDS are executed.

for ((: for (( exp1; exp2; exp3 )); do COMMANDS; done
    Equivalent to
        (( EXP1 ))
        while (( EXP2 )); do
            COMMANDS
            (( EXP3 ))
        done
    EXP1, EXP2, and EXP3 are arithmetic expressions.  If any expression is
    omitted, it behaves as if it evaluates to 1.

2 Comments

Re: If `in WORDS ...;' is not present, then `in "$@"' is assumed: this is what I was looking for. Thanks!
Here is example with using for loop without [in WORDS ...;] construction
10

I commonly like to use a slight variant on the standard for loop. I often use this to run a command on a series of remote hosts. I take advantage of Bash's brace expansion to create for loops that allow me to create non-numerical for loops.

Example:

I want to run the uptime command on frontend hosts 1-5 and backend hosts 1-3:

% for host in {frontend{1..5},backend{1..3}}.mycompany.com
    do ssh $host "echo -n $host; uptime"
  done

I typically run this as a single-line command with semicolons on the ends of the lines instead of the more readable version above. The key usage consideration are that braces allow you to specify multiple values to be inserted into a string (e.g. pre{foo,bar}post results in prefoopost, prebarpost) and allow counting/sequences by using the double periods (you can use a..z, etc.). However, the double period syntax is a new feature of Bash 3.0; earlier versions will not support this.

2 Comments

Related to this, what if $host var is an empty line by some accident? It would still fire ssh. So, how do I avoid that? In my case I'm doing something different and so your answer will help me. I'm trying to check Gmail for new messages, and if found, send an SMS of from and subject. My question is labeled "Connecting Two Bash Commands" if you want to read it.
I don't believe you would get an empty $host var. This is because the example above is using brace expansion. The for loop explicitly sets the $host variable to values: frontend1.mycompany.com frontend2.mycompany.com . . backend1.mycompany.com But if you see a way for it to have a null value; I'd be interested to know. You could execute the ssh inside a conditional test if it was a concern.
7
#! /bin/bash

function do_something {
   echo value=${1}
}

MAX=4
for (( i=0; i<MAX; i++ )) ; {
   do_something ${i}
}

Here's an example that can also work in older shells, while still being efficient for large counts:

Z=$(date) awk 'BEGIN { for ( i=0; i<4; i++ ) { print i,"hello",ENVIRON["Z"]; } }'

But good luck doing useful things inside of awk: How do I use shell variables in an awk script?

1 Comment

I didn't notice it before, but a similar syntax is shown in one of the earlier answers. The unique thing here is the use of curly-braces instead of the typical do/done pair.
4

If you're interested only in Bash, the "for(( ... ))" solution presented above is the best, but if you want something POSIX SH compliant that will work on all Unices, you'll have to use "expr" and "while", and that's because "(())" or "seq" or "i=i+1" are not that portable among various shells.

Comments

1

I use variations of this all the time to process files...

for files in *.log; do echo "Do stuff with: $files"; echo "Do more stuff with: $files"; done;

If processing lists of files is what you're interested in, look into the -execdir option for files.

1 Comment

I think you mean -execdir option for find

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.