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$variable
do
anddone
– 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 for
is 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
count
less 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 $count
and 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 while
and until
.
For example, a loop from a really used script that checks the status of the Tomcat server (the PID
taken 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).