BASH: using loops – for, while, until, with examples

By | 01/15/2023

terminal

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.

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 loop
  • variable– we declare a variable on which we will perform actions
  • in – 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
  • doand done – 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 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 $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).