The for loop

The for loop can be considered the more powerful loop in Bash scripting. In practice, for and while are interchangeable, but for has better shorthand syntax. This means that to write a loop in for often requires much less code than an equivalent while loop.

The for loop has two different syntaxes: a C-style syntax and the regular Bash syntax. We'll first look at the Bash syntax:

FOR value IN list-of-values DO thing-with-value DONE

A for loop allows us to iterate over a list of things. Each loop will use a different item in that list, in a sequential order. This very simple example should illustrate this behavior:

reader@ubuntu:~/scripts/chapter_11$ vim for-simple.sh
reader@ubuntu:~/scripts/chapter_11$ cat for-simple.sh
#!/bin/bash

#####################################
# Author: Sebastiaan Tammer
# Version: v1.0.0
# Date: 2018-10-27
# Description: Simple for syntax.
# Usage: ./for-simple.sh
#####################################

# Create a 'list'.
words="house dog telephone dog"

# Iterate over the list and process the values.
for word in ${words}; do
echo "The word is: ${word}"
done

reader@ubuntu:~/scripts/chapter_11$ bash for-simple.sh
The word is: house
The word is: dog
The word is: telephone
The word is: dog

As you can see, for takes a list (in this case, a string delimited by whitespace), and for each value it finds it performs the echo action. We've added some extra text there so that you can see that it actually goes into the loop four times and does not just print the list with extra new lines. The main thing to notice here is that in the echo we use the ${word} variable, which we defined as the second word in the for definition. This means that for every run of the for loop, the value of the ${word} variable is different (which is very much using a variable as it is intended, with a variable content!). You can name this anything, but we prefer to give semantically logical names; since we called our list words, an item in that list would be a word.

If you want to do the same thing with while, things are going to get a lot more complicated. It's definitely possible by using a counter and a command such as cut (which allows you to cut out different parts of a string), but since the for loop does it in this simple manner, why bother?

The second syntax that we can use with for will be more recognizable for those experienced with other scripting programming languages. This C-style syntax uses a counter that increments until a certain point, not unlike the example we saw when we looked at while. The syntax is as follows:

FOR ((counter=0; counter<=10; counter++)); DO something DONE

Seems pretty similar right? Check out this example script:

reader@ubuntu:~/scripts/chapter_11$ vim for-counter.sh 
reader@ubuntu:~/scripts/chapter_11$ cat for-counter.sh
#!/bin/bash

#####################################
# Author: Sebastiaan Tammer
# Version: v1.0.0
# Date: 2018-10-27
# Description: Example of a for loop in C-style syntax.
# Usage: ./for-counter.sh
#####################################

# This loop runs 10 times.
for ((counter=1; counter<=10; counter++)); do
echo "Hello! This is loop number ${counter}."
sleep 1
done

# After the for-loop finishes, print a goodbye message.
echo "All done, thanks for tuning in!"

reader@ubuntu:~/scripts/chapter_11$ bash for-counter.sh
Hello! This is loop number 1.
Hello! This is loop number 2.
Hello! This is loop number 3.
Hello! This is loop number 4.
Hello! This is loop number 5.
Hello! This is loop number 6.
Hello! This is loop number 7.
Hello! This is loop number 8.
Hello! This is loop number 9.
Hello! This is loop number 10.
All done, thanks for tuning in!

Again, due to the nature of off-by-one errors, we have to use slightly different numbers. Since the counter is incremented at the end of the loop, we need to start it at 1 instead of 0 (or we could have done the same in the while loop). In C-style syntax, <= means smaller than or equal to, and ++ means increment by 1. So, we have a counter that starts at 1, continues until it reaches 10, and is incremented by 1 for each run of the loop. We find this for loop preferable to the equivalent while loop; it needs less code and is more common in other scripting/programming languages.

Even better, there is a way to iterate over a number range (as we did for 1–10 previously), with the for loop Bash syntax as well. Because a number range is nothing more than a list of numbers, we can use almost the same syntax as we did in the first example, in which we iterated over a list of words. Take a look at the following code:

reader@ubuntu:~/scripts/chapter_11$ vim for-number-list.sh
reader@ubuntu:~/scripts/chapter_11$ cat for-number-list.sh
#!/bin/bash

#####################################
# Author: Sebastiaan Tammer
# Version: v1.0.0
# Date: 2018-10-27
# Description: Example of a for loop with a number range.
# Usage: ./for-number-list.sh
#####################################

# This loop runs 10 times.
for counter in {1..10}; do
echo "Hello! This is loop number ${counter}."
sleep 1
done

# After the for-loop finishes, print a goodbye message.
echo "All done, thanks for tuning in!"

reader@ubuntu:~/scripts/chapter_11$ bash for-number-list.sh
Hello! This is loop number 1.
Hello! This is loop number 2.
Hello! This is loop number 3.
Hello! This is loop number 4.
Hello! This is loop number 5.
Hello! This is loop number 6.
Hello! This is loop number 7.
Hello! This is loop number 8.
Hello! This is loop number 9.
Hello! This is loop number 10.
All done, thanks for tuning in!

So, the syntax for <variable> in <list> works with a list of {1..10}. This is called brace expansion and was added in Bash version 4. The syntax for brace expansion is quite simple:

{<starting value>..<ending value>}

Brace expansion can be used in many ways, but printing lists of numbers or characters is the most well-known:

reader@ubuntu:~/scripts/chapter_11$ echo {1..5}
1 2 3 4 5
reader@ubuntu:~/scripts/chapter_11$ echo {a..f}
a b c d e f

The brace expansion {1..5} returns the string 1 2 3 4 5, which is a whitespace delimited list of values and can thus be used in the Bash-style for loop! Alternatively, {a..f} prints the string a b c d e f. The range is actually determined by ASCII hexadecimal codes; this allows us to do the following as well:

reader@ubuntu:~/scripts/chapter_11$ echo {A..z}
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z

It might seem weird that you'll see some special characters printed halfway, but those are in-between the uppercase and lowercase Latin alphabet characters. Note that this syntax is very similar to getting the value of a variable with ${variable} (however, that is parameter expansion and not brace expansion).

The brace expansion has one other interesting piece of functionality: it allows us to define the increment! Simply put, this allows us to tell Bash how many steps to skip each time we increment. The syntax for this is as follows:

{<starting value>..<ending value>..<increment>}

By default, the increment value is 1. If this is the desired functionality, we can omit the increment value, as we previously saw. If we do set it, however, we'll see something like the following:

reader@ubuntu:~/scripts/chapter_11$ echo {1..100..10}
1 11 21 31 41 51 61 71 81 91
reader@ubuntu:~/scripts/chapter_11$ echo {0..100..10}
0 10 20 30 40 50 60 70 80 90 100

Now, the increment is done in steps of 10. As you can see in the previous example, the <ending value> is considered inclusive. This means that values that are lower or equal will be printed, but others will not. The next value in the first brace expansion in the preceding example. {1..100..10}, would have been 101; since this is not lower or equal to 100, the value is not printed and the expansion is terminated.

Finally, since we promised that anything we could do with while we could also do with for, we'd like to end this part of the chapter by showing you how you would create an infinite loop with for. This is the most common reason to choose while over for, because the for syntax is a little weird:

eader@ubuntu:~/scripts/chapter_11$ vim for-infinite.sh 
reader@ubuntu:~/scripts/chapter_11$ cat for-infinite.sh
#!/bin/bash


#####################################
# Author: Sebastiaan Tammer
# Version: v1.0.0
# Date: 2018-10-27
# Description: Example of an infinite for loop.
# Usage: ./for-infinite.sh
#####################################

# Infinite for loop.
for ((;;)); do
echo "Hello!"
sleep 1 # Wait for 1 second.
done

reader@ubuntu:~/scripts/chapter_11$ bash for-infinite.sh
Hello!
Hello!
Hello!
^C

We use the C-style syntax, but we omit the initialization, comparison, and incrementing of the counter. Therefore, it reads as follows:

for ((<nothing>;<no-comparison>;<no-increment>)); do

This ends up as ((;;));, which only makes sense if you put it in the context of the normal syntax, as we did in the previous example. We could also just omit either the increment or the comparison to the same effect, but that would do the same thing with more code. Often, shorter is better, since it will be clearer.

Try to replicate the infinite for loop, but only by omitting a single value from the for clause. If you get that working, you'll be a step closer to understanding why you have now made it unending. If you need a little nudge, perhaps you'd want to echo the value of counter in the loop so that you can see what is happening. Or you could always run it with bash -x, of course!

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.116.88.157