Key Takeaways
- The Bash for loop is a versatile tool that can work with numbers, words, arrays, command line variables, or the output of other commands.
- Simple for loops can be used with numerical lists or word lists, allowing you to repeat a section of code for each value in the list.
- The for loop can also be used with number ranges, filenames, command line parameters, word arrays, and associative arrays, providing flexibility in scripting tasks.
The versatile Bash for
loop does much more than loop around a set number of times. We describe its many variants so you can use them successfully in your own Linux scripts.
The for Loop
All scripting and programming languages have some way of handling loops. A loop is a section of code that you want to have executed repeatedly. Rather than type the same set of instructions into your script, again and again, a loop will repeat one section of code over and over for you.
The Bash for
loop is very flexible. It can work with numbers, words, arrays, command line variables, or the output of other commands. These are used in the header of the loop. The header dictates what the loop is working with—numbers or strings, for example—and what the end condition is that will stop the looping.
The body of the loop contains the code that you want to have repeated. It holds what you want the loop to do. The loop body can contain any valid script command.
A variable called the loop counter or iterator is used to step through a range of values or a list of data items. For each loop, the iterator takes on the value of the next number, string, or whatever data type the loop is iterating over. This allows the loop to work with the values of each of the data items in turn, or even in some cases to manipulate the data items themselves.
Simple for Loops
If you’re looking to write your first for loop, these simple examples will get you started.
for Loops using Numerical Lists
You can run a for
loop on the command line. This command creates and executes a simple for
loop. The iterator is a variable called i
. We’re going to assign i
to be each of the values in the list of numbers, in turn. The body of the loop is going to print that value to the terminal window. The condition that ends this loop is when i
has iterated across the entire list of numbers.
for i in 1 2 3 4 5; do echo $i; done
It’s important to note here that the variable i
is increased by one each time the loop spins round, but that’s because the list of numbers goes up by one each time.
This list of numbers starts at 3 and goes up in steps of two, then arbitrarily leaps to 44.
for i in 3 5 7 9 11 44; do echo $i; done
It makes no difference to the for
loop. It starts at one end of the list and uses each value in turn, until all the values in the list have been used.
Nor do the numbers need to be in ascending order. They can be in any order.
for i in 3 43 44 11 9; do echo $i; done
for Loops Using Word Lists
We can just as easily do the same with words. Copy the text of the script into an editor and save it as “word-list.sh.”
#!/bin/bash
for word in This is a sequence of words
do
echo $word
done
You’ll need to use chmod
to make the script executable, and any other script you copy out of this article. Just substitute the name of the script each time you use the chmod
command.
chmod +x word-list.sh
Let’s run the script.
./word-list.sh
Just as it did with the numbers, the iterator—in this example, the variable word
—works its way through the list of data items until it reaches the end of the list. The loop body accesses the value in the word
variable and so each word in the list gets processed.
for Loops with Number Ranges
If you wanted a for
loop to run 100 times it would be a pretty tiresome affair to have to type in a sequence of 100 numbers in the loop header. Number ranges let you specify the first and last number only.
This script is “number-range.sh.”
#!/bin/bash
for i in {1..10}
do
echo "Loop spin:" $i
done
The number range is defined within curly brackets “{}
” with two periods “..
” separating the numbers that start and end the range. Make sure you don’t include any whitespace in the range definition.
This is how it runs:
./number-range.sh
You can include another number that defines the step size the iterator should use to walk through the numbers in the range. This script, “number-range2.sh” will use a range of 0 to 32, and a step size of 4.
#!/bin/bash
for i in {0..32..4}
do
echo "Loop spin:" $i
done
The iterator steps through the number range in jumps of four.
./number-range2.sh
for Loops Using Filenames
Because we can process lists of words, we can get our scripts to work with filenames. This script is called “filenames.sh.”
#!/bin/bash
for file in word-list.sh number-range.sh number-range2.sh filenames.sh
do
ls -lh "$file"
done
It would be pretty pointless to have a script that only does what ls
can do, but it does demonstrate how to access filenames inside the loop body.
./filenames.sh
In a similar way to using the number range, we can use a file pattern in the loop header to specify the files we want to process. This avoids a lot of typing and means we don’t need to know in advance the names of the files.
This script is called “filenames2.sh.” We’ve replaced the list of filenames with the filename pattern “*.sh” to have the script report on all script files in the current directory.
#!/bin/bash
for file in *.sh
do
ls -lh "$file"
done
Here’s the output.
./filenames2.sh
for Loops Using Command Line Parameters
We can add some more flexibility by passing in the filename pattern on the command line. The $*
variable represents all of the command line parameters passed to the script.
This is “filenames3.sh.”
#!/bin/bash
for file in $*
do
ls -lh "$file"
done
We’ll ask for filenames that begin with “n” and have an SH extension.
./filenames3.sh n*.sh
We can also pass in more than one pattern at a time.
./filenames3.sh n*.sh .bashrc
The iterator variable file
takes on the value of each of the command line parameters. Filename patterns are expanded, and all of the filenames are processed in the loop body.
C-like for Loops
Bash supports the classic three-term for loop, such as those found in the C programming language. They’re called three-term for loops because there are three terms in the loop header.
- The initial value of the loop iterator.
- The test for whether the loop continues or ends.
- The incrementing—or decrementing—of the iterator.
This script is “c-like.sh.”
The iterator I
is set to 1 at the start of the loop, and the loop will run for as long as the statement ” i<=10
” is true. As soon as i
reaches 11, the for
loop will stop. The iterator is being increased by one, every revolution of the loop.
#!/bin/bashfor (( i=1; i<=10; i++ ))
do
echo "Loop number:" $i
done
Let’s run this script.
./c-like.sh
The C-like for
loop permits the easy creation of for
loops that have slightly odd requirements. This loop starts at 15, and counts backward in steps of 3. This is “c-like2.sh”
#!/bin/bash
for (( i=15; i>0; i-=3 ))
do
echo "Loop number:" $i
done
When we run it, it should jump backward in steps of three.
./c-like2.sh
Infinite for Loops
You can also use this format of for
loop to create an infinite loop. All you need do is remove all of the elements from the loop header, like this. This is “infinite.sh.”
#!/bin/bash
for (( ; ; ))
do
echo "Press Ctrl+C to stop..."
sleep 1
done
You’ll need to hit Ctrl+C to stop the loop.
./infinite.sh
for Loops Using Word Arrays
We can easily iterate through an array of words. We need to provide the name of the array in the loop header, and the iterator will walk through all entries in the array. This is “word-array.sh.”
#!/bin/bash
distributions=("Ubuntu Fedora Manjaro Arch EndeavourOS Garuda")
for distro in $distributions
do
echo $distro
done
All the distributions are listed for us.
./word-array.sh
The continue Command
If you want the loop to step over a particular entry, test whether the iterator matches that entry and use the continue
command. The continue
command abandons the current spin of the loop. It increments the iterator and starts the next spin of the loop—assuming the entry you want to skip over isn’t the last item in the list.
This is “word-array2.sh.” It steps over the “Arch” array entry but processes all other array members.
#!/bin/bash
distributions=("Ubuntu Fedora Manjaro Arch EndeavourOS Garuda")
for distro in $distributionsdo
if [[ "$distro" == "Arch" ]] ;
then
continue
fi
echo $distro
done
“Arch” doesn’t appear in the terminal window.
./word-array2.sh
The break Command
The break
command breaks out of the loop and prevents any more processing.
This is “word-array3.sh.” It’s the same as the previous script with continue
replaced by break
.
#!/bin/bash
distributions=("Ubuntu Fedora Manjaro Arch EndeavourOS Garuda")
for distro in $distributions
do
if [[ "$distro" == "Arch" ]] ;
then
break
fi
echo $distro
done
When the iterator contains “Arch” the for loop abandons any more processing.
./word-array3.sh
for Loops Using Associative Arrays
In Bash 4 and higher, associative arrays allow you to create lists of key-value pairs that can be searched by the key or by the value. Because of the two-way relationship between the key and the value, they’re also called data dictionaries.
We can iterate through an associative array using a for
loop. This script is “associative.sh.” It defines an associative array with four entries in it, one for each of “dog”, “cat”, “robin” , and “human.” These are the keys. The values are the (default) number of legs they each have.
#!/bin/bashdeclare -A animals=( [dog]=Four-legged [cat]=Four-legged [robin]=Two-legged [human]=Two-legged )
for legs in ${!animals[@]}
do
if [ ${animals[$legs]} == "Two-legged" ]; then
echo ${legs}
fi
done
The iterator is called legs
. Note that the loop header contains an “!
” exclamation point. This is not acting as the logical NOT operator, it’s part of the associative array syntax. It is required to search through the array.
The body of the loop performs a string comparison test. If the value of the array member is “Two-legged”, it prints the key value to the terminal window. When we run it, the script prints the two-legged creatures.
./associative.sh
Iterating Over the output of Commands
If you have a command or sequence of commands that produce a list of something, such as filenames, you can iterate through them with a for
loop. You need to watch out for unexpected filename expansions, but in simple cases it is fine.
This script is “command.sh.” it uses ls
and wc
to provide a sorted list of script file names, together with their line, word, and byte counts.
#!/bin/bashfor i in $(ls *.sh | sort);do echo $(wc $i)done
When we run it we get the statistics for each file, with the files listed in alphabetical order.
./command.sh
The Dazzling for Loop
The for
loop is a versatile and easily understood scripting tool. But as flexible as it is, don’t forget that other loops exist for a reason. Don’t be dazzled into thinking the for
loop is all you’ll ever need.
The while
loop, for example, is much better suited for certain things than the for
loop, such as reading lines from a file.
Writing good scripts means using the most suitable tool for the task at hand. The for
loop is a great tool to have in your toolbox of tricks.