For the final part of this chapter, we'd like to show you how you can influence nested loops with loop control as well. Both break and continue will take an extra argument: a number that specified which loop to break out of. By default, if this argument is omitted, it is assumed to be 1. So, the break command is equal to break 1, and continue 1 is the same as continue. As stated before, we can theoretically nest our loops as deep as we want; you're likely to hit logical issues a lot earlier than problems with the technical capabilities of your modern system! We'll look at a simple example that shows us how we can use a break 2 to not only break out of a for loop, but out of the outer while loop as well:
reader@ubuntu:~/scripts/chapter_11$ vim break-x.sh
reader@ubuntu:~/scripts/chapter_11$ cat break-x.sh
#!/bin/bash
#####################################
# Author: Sebastiaan Tammer
# Version: v1.0.0
# Date: 2018-10-28
# Description: Breaking out of nested loops.
# Usage: ./break-x.sh
#####################################
while true; do
echo "This is the outer loop."
sleep 1
for iteration in {1..3}; do
echo "This is inner loop ${iteration}."
sleep 1
done
done
echo "This is the end of the script, thanks for playing!"
This first version of the script does not contain break. When we run this, we never see the final message and we get an endless repeating pattern:
reader@ubuntu:~/scripts/chapter_11$ bash break-x.sh
This is the outer loop.
This is inner loop 1.
This is inner loop 2.
This is inner loop 3.
This is the outer loop.
This is inner loop 1.
^C
Now, let's break the inner loop when the iteration hits 2:
reader@ubuntu:~/scripts/chapter_11$ vim break-x.sh
reader@ubuntu:~/scripts/chapter_11$ cat break-x.sh
#!/bin/bash
#####################################
# Author: Sebastiaan Tammer
# Version: v1.1.0
# Date: 2018-10-28
# Description: Breaking out of nested loops.
# Usage: ./break-x.sh
#####################################
<SNIPPED>
for iteration in {1..3}; do
echo "This is inner loop ${iteration}."
if [[ ${iteration} -eq 2 ]]; then
break 1
fi
sleep 1
done
<SNIPPED>
When we run the script now, we still get infinite loops, but we're cutting the inner for loop short after two iterations instead of three:
reader@ubuntu:~/scripts/chapter_11$ bash break-x.sh
This is the outer loop.
This is inner loop 1.
This is inner loop 2.
This is the outer loop.
This is inner loop 1.
^C
Now, let's instruct the inner loop to break out of the outer loop by using the break 2 command:
reader@ubuntu:~/scripts/chapter_11$ vim break-x.sh
reader@ubuntu:~/scripts/chapter_11$ cat break-x.sh
#!/bin/bash
#####################################
# Author: Sebastiaan Tammer
# Version: v1.2.0
# Date: 2018-10-28
# Description: Breaking out of nested loops.
# Usage: ./break-x.sh
#####################################
<SNIPPED>
if [[ ${iteration} -eq 2 ]]; then
break 2 # Break out of the outer while-true loop.
fi
<SNIPPED>
Behold, an inner loop breaking out of an outer loop successfully:
reader@ubuntu:~/scripts/chapter_11$ bash break-x.sh
This is the outer loop.
This is inner loop 1.
This is inner loop 2.
This is the end of the script, thanks for playing!
There we go, full control over our loops, even when we nest as many as we need for our scripting needs. The same theory applies to continue as well. If, in this example, we use continue 2 instead of break 2, we would still get an infinite loop (since while true never ends). However, if your other loop was also a for or a non-infinite while loop (which, in our experience, is more common but does not make for a great simple example), continue 2 could allow you to execute exactly the logic that the situation desires.