5 ways to run a loop with a number of times in Bash
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 to finish 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 a number of times in bash. At the end of the post, there will be one more method (the 6th method). Its behavior is a bit different from the others; it is a loop with a 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 shells 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 keeps 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 ignores the sign (and take the absolute value). Zsh does the same but reverses 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