You have ever written bash scripts to start a server, and send a probe request to check for the sever's healthy. However, you want your script finishes right after it detects the server alive, or, after a number of tries, or, after a limited time (in seconds).

The idea of making a loop comes to your mind, before you will think about how to avoid sending too many requests to the server.

In this post, I will summarize 5 ways to make a for-loop with a number of times in bash. At the end of the post, there will be one more method (6th method). Its behavior is a bit different from the others; it is a loop with time limit (in second) condition, instead of the running count condition.


Method 1: Brace expansion

In bash, if you write {1..10}, it is expanded to 1 2 3 4 5 6 7 8 9 10. This can be used to make a for-loop.

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

Note 1: to make a reverse for-loop, use {10..1}, it is expanded to 10 9 8 7 6 5 4 3 2 1.

Note 2: with bash, the variable cannot be used in the place of 10 or 1, because the brace expansion happens very early and before variable expansion (source). Other shell such as zsh can interpret and evaluate the value before trying the sequence expansion.

Note 3: from bash 4.0, the step size can be specified. For example

# echo {1..10..2}
1 3 5 7 9

Note 4: if the step size is zero, bash treats the step as one, while zsh treats the whole expansion as invalid and keep it as literal.

bash# echo {1..3..0}
1 2 3
zsh# echo {1..3..0}
{1..3..0}

Note 5: if the step size is negative, bash ignore the sign (and take the absolute value). Zsh does the same but reverse the loop order.

bash# echo {5..1..-2}
5 3 1
zsh# echo {5..1..-2}
1 3 5

Method 2: use seq command

Example:

for i in $(seq 5); do
	echo $i
done

The seq command can be used in the following ways:

# echo $(seq 5)
1 2 3 4 5
# echo $(seq 3 10)
3 4 5 6 7 8 9 10
# echo $(seq 3 2 10)
3 5 7 9

The 5 value can be replaced with a variable, such as $(seq $count).

Note: for more usage of seq, use man seq.

Method 3: C-style for loop

Example:

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

Method 4: use while loop

Example 1:

i=0
while [[ $i -lt 10 ]]; do
	echo $i;
    ((i++));
done

Example 2:

i=0
while ((i < 10)); do
	echo $i;
    ((i++));
done

Method 5: use until loop

Example 1:

i=0
until [[ $i -ge 10 ]]; do
	echo $i;
    ((i++));
done

Example 2:

i=0
until ((i >= 10)); do
	echo $i;
    ((i++));
done

Method 6:

As I noted initially this method does not always run exactly by the number of times specified. It is rather used to limit the running time in total by using the bash variable named SECONDS.

This variable expands to the number of seconds since the shell was started. Assignment to this variable resets the count to the value assigned, and the expanded value becomes the value assigned plus the number of seconds since the assignment. The number of seconds at shell invocation and the current time is always determined by querying the system clock. If SECONDS is unset, it loses its special properties, even if it is subsequently reset.

Example

time_limit_in_sec=10
begin=$SECONDS
while ((SECONDS < begin + time_limit_in_sec)); do
	date
    sleep 1
done

The date command can be used to achieve the same purpose:

time_limit_in_sec=10
begin=$(date +%s)
while (($(date +%s) < begin + time_limit_in_sec)); do
	date
    sleep 1
done