
This a translation of a post from 2013 with some edits, but still relevant for learning BASH.
Brief description of the difference in the Bash loop types:
for– will perform an action as long as there are objects to perform (for example, reading a stream from a stdin, file, or function);
while– will perform an action as long as the condition is true;
until– will be executed until the condition becomes true, i.e. while it is false.
Contents
FOR loop
Consider this version of a bash script with the for loop:
#!/bin/bash
for variable in `ls -1`
do
echo "$variable"
done
The syntax is very simple and is quite clearly shown in the example:
for– start the loopvariable– we declare a variable on which we will perform actionsin– direct the flow to the loop`ls -1`– the command that needs to be executed and the output will be passed to the variable$variabledoanddone– the “body” of the cycle, within which the main actions will be performed on the received data- and
echo "$variable"– the action itself performed by the loop
Now let’s change the example a little, and instead of explicitly specifying the command, we will apply a second variable:
#!/bin/bash
ls=`ls -1`
for variable in $ls
do
echo "$variable"
done
Now the command ls -1 is passed in a separate variable, which allows more flexible work with the loop. Instead of a variable in a loop, you can also use a function:
#!/bin/bash
lsl () {
ls -1
}
for variable in `lsl`
do
echo "$variable"
done
See more about functions in Bash in the BASH: использование функций, примеры post.
The main condition of the cycle foris that it will be executed as long as the command passed to it has objects for action. Based on the example above – as long as in the ls -1 output there are files to display in the listing, the loop will pass them to a variable and execute the “loop body”. As soon as the list of files in the directory ends, the loop will complete its execution.
Let’s complicate the example a bit.
The directory contains a list of files:
[simterm]
$ ls -1 file1 file2 file3 file4 file5 loop.sh nofile1 nofile2 nofile3 nofile4 nofile5
[/simterm]
We need to select from them only those that do not have the word “no” in the name:
#!/bin/bash
lsl=`ls -1`
for variable in $lsl
do
echo "$variable" | grep -v "no"
done
Run the script:
[simterm]
$ ./loop.sh file1 file2 file3 file4 file5 loop.sh
[/simterm]
In a loop, you can also use conditional expressions to test conditions and the break operator to stop the loop execution if a condition is triggered.
Consider this example:
#!/bin/bash
lsl=`ls -1`
for variable in $lsl
do
if [ $variable != "loop.sh" ]
then
echo "$variable" | grep -v "no"
else
break
fi
done
The loop will run until a file is encountered loop.sh. As soon as the loop execution reaches this file, the loop will be interrupted by the command break:
[simterm]
$ ./loop.sh file1 file2 file3 file4 file5
[/simterm]
Another example is the use of arithmetic operations just before the execution of the loop body:
#!/bin/bash
for (( count=1; count<11; count++ ))
do
echo "$count"
done
Here we set three control commands:
count=1– the controlling condition- while
countless than 11 - and the command to execute –
count +1:
[simterm]
$ ./loop.sh 1 2 3 4 5 6 7 8 9 10
[/simterm]
WHILE loop
A simple example that demonstrates how the while loop works:
#!/bin/bash
count=0
while [ $count -lt 10 ]
do
(( count++ ))
echo $count
done
We set the $count variable to zero, after which we run the while loop with the condition “while $count less than ten – execute the loop”. In the body of the loop, we perform a postfix increment +1 to the variable $countand outputs the result to stdout.
Execution result:
[simterm]
$ ./loop.sh 1 2 3 4 5 6 7 8 9 10
[/simterm]
As soon as the value of the variable $count became 10, the loop will be stopped.
Infinite loops in Bash
A good example of the “infinite” loop that demonstrates how the while is working:
#!/bin/bash
count=10
while [ 1 = 1 ]
do
(( count++ ))
echo $count
done
Run the script:
[simterm]
$ ./loop.sh ... 5378 5379 5380 5381 5382 5383 ^C
[/simterm]
UNTIL loop
Similarly, but “in the opposite direction”, the until loop is working:
#!/bin/bash
count=0
until [ $count -gt 10 ]
do
(( count++ ))
echo $count
done
Here we set a similar condition, but instead of “while the variable is less than 10“, we are using “until the variable becomes greater than 10”.
Execution result:
[simterm]
$ ./loop.sh 1 2 3 4 5 6 7 8 9 10 11
[/simterm]
If the above example of the infinite loop is executed using the until, unlike while it will not output anything:
#!/bin/bash
count=10
until [ 1 = 1 ]
do
(( count++ ))
echo $count
done
Run it:
[simterm]
$ ./loop.sh $
[/simterm]
Since the condition initially is set to the true, the body of the loop will not be executed.
As in the for loop, functions can be used in whileand until.
For example, a loop from a really used script that checks the status of the Tomcat server (the PIDtaken in the SLES operating system, so it may differ in others), a slightly simplified version:
#!/bin/bash
check_tomcat_status () {
RUN=`ps aux | grep tomcat | grep -v grep | grep java | awk '{print $2}'`
}
while check_tomcat_status
do
if [ -n "$RUN" ]
then
printf "WARNING: Tomcat still running with PID $RUN."
else
printf "Tomcat stopped, proceeding...nn"
break
fi
done
And its execution result:
[simterm]
$ ./loop.sh WARNING: Tomcat still running with PID 14435 26548.WARNING: Tomcat still running with PID 14435 26548.WARNING: Tomcat still running with PID 14435 26548.WARNING: Tomcat still running with PID 14435 26548.WARNING: Tomcat still running with PID 14435 26548.WARNING: Tomcat still running with PID 14435 26548.WARNING: Tomcat still running with PID 14435 26548.WARNING: Tomcat still running with PID 14435
[/simterm]
The full script, without simplifications:
#!/bin/bash
check_tomcat_status () {
RUN=`ps aux | grep tomcat | grep -v grep | grep java | awk '{print $2}'`
}
while check_tomcat_status; do
if [ -n "$RUN" ]
then
printf "WARNING: Tomcat still running with PID $RUN. Stop it? "
answer "Stopping Tomcat..." "Proceeding installation..." && $CATALINA_HOME/bin/shutdown.sh 2&>1 /dev/null || break
sleep 2
if [ -n "$RUN" ]
then
printf "Tomcat still running. Kill it? "
answer "Killing Tomcat..." "Proceeding installation...n" && kill $RUN || break
sleep 2
fi
else
printf "Tomcat stopped, proceeding...nn"
break
fi
done
Here is the answer() function:
answer () {
while read response; do
echo
case $response in
[yY][eE][sS]|[yY])
printf "$1n"
return 0
break
;;
[nN][oO]|[nN])
printf "$2n"
return 1
break
;;
*)
printf "Please, enter Y(yes) or N(no)! "
esac
done
}
It was possible to use both while and until loops, but not for, since it would work only once (received PID– and ended).




