facebook youtube pinterest twitter reddit whatsapp instagram

Iterating With Loops (Bash)

Imagine having to edit 10,000 lines of text within a CSV file, this would be a shit load of work if done manually, which is where looping comes in. When a task or a series of tasks needs to be repeated, it can be put inside a loop.

In this guide, we would look at for, while, and until loops in bash. The for loops through a list of words, while and until executes until a condition is either true or false.

Starting With...

For Loops

The for is an inbuilt shell keyword, you can verify by doing the following:

user@server:~$ type for
for is a shell keyword

Since, this is a shell keyword, you can either use it in a script and even directly at the command line. The for loop lets you iterate (perform again) over a series of words within a string or a list of files. Consider the following example:

for f in $( ls ); do
    echo "file name is" : $f
done

We started with the for keyword, and we then declare f to be the variable that will take different values contained in $( ls ), what we are basically doing is reading from the list on the right to populate the variable parameter on the left.

Imagine, you have files in your directory with the following name:

  • cat
  • dog
  • hen
  • egg

The ls command (The ‘ls’ is used to list information about files, e.g file names) would pick the first file name, in this case, cat, it would store it in the f variable, in the second line, we are echoing the output of the f variable, and lastly, the done keyword indicates that the code that used the value of $f has finished and $f can take a new value, so, the next value would be 'dog', and it would repeat the same process until it gets to 'egg', that is the idea of a for loop, as long as there are items to be processed in the list, the loop will execute until the list is exhausted.

This is the output of the above command:

user@server:~$ for f in $( ls ); do
> echo "file name is" : $f
> done
file name is : cat
file name is : dog
file name is : egg
file name is : hen

Take another example:

for i in 1 2 3 4; do
   echo "Number $i"
done

The above command looks pretty easy, the i variable would pick and store the first item, in this case, '1', in the second line, we are echoing the output of the i variable, and lastly, the done keyword indicates that the code that used the value of $i has finished and $i can take a new value, so, the next value would be '2', and it would repeat the same process until it gets to '4'. Here is the output:

user@server:~$ for i in 1 2 3 4; do
> echo "Number $i"
> done
Number 1
Number 2
Number 3
Number 4

You can also use brace expansion to speed things up, it can save you a ton of time when working with a range of terms.

For example, to specify an interval of the number we would be using, we can do the following:

user@server:~/bin$ echo {1..9}
1 2 3 4 5 6 7 8 9

In for loop, you do:

for i in {1..9}; do
   echo "Number $i"
done

To create a number between 1 and 10, counting by two we do:

user@server:~$ echo {1..10..2}
1 3 5 7 9

In for loop, you do:

for i in {1..10..2}; do
   echo "Number $i"
done

To create by 3, you do:

user@server:~/cat$ echo {1..10..3}
1 4 7 10

In for loop, you do:

for i in {1..10..3}; do
   echo "Number $i"
done

Here are more examples of for loops:

for u in james joy john william ; do
    useradd -d /home/$u -m $u
    echo "$u:defaultpass" | chpasswd
    passwd -e $u
done

In the above command, we read from the list on the right to populate the variable parameter on the left, in this case, james, joy and so on. Practically, this is what the above script is doing:

  • Create the user james, and create the user home directory /home/james
  • Set the password for james
  • Expire the password so it will need to be reset on the first login for the user james

We then loop back and repeat the process for the user joy, john, and william.

Using Dynamic List With for loop

So far, we haven't really been discussing about dynamic list when using for loop.  To create dynamic lists, we use globbing techniques to populate the list. Consider the following example:

for f in * ; do
ls -la "$f"
done

The above command would list all the files in the current directory

When a list is generated, such as with file globbing, we should quote the expansion of the variable parameter. Without the quotes, it is
possible that a space will get included that will cause the command to fail.

You can also isolate a specific file names, e.g book:

for f in book* ; do
ls -la "$f"
done

This would list any files that starts with book, to isolate a specific extension, e.g jpg, you can do:

for f in *.jpg ; do
ls -la "$f"
done

Controlling the Loop with Break and Continue

What if at some point, we need to exit the loop prematurely or perhaps exclude certain items from processing, e.g you might only want to process only a file without touching the directory. For this case, we have loop control keywords, such as break and continue.

The break keyword is used to break out of a loop, be it a for or while loop, the continue keyword is used to end the current iteration in a loop, but continue with the next.

Let's take a couple of examples, a for loop to only process files:

for f in * ; do
[ -f "$f" ] || continue
chown root:root "$f"
done

The above script changes the user and group and all the files in the current directory, this is what we are doing:

  • The * search will return all files
  • [ -f "$f" ] - this ensures we are only processing files, and if it is not a file e.g if it is a directory, the continue keyword would skip the remaining commands inside the body of the enclosing loop for the current iteration and passes program control to the next iteration of the loop.
  • If it is a file, we change the user and group to root

If we need to run the loop until we found a file and then exit the loop immediately,  we can adjust the code so that we can iterate through each file. If the file is a file then we exit the loop with the break keyword:

for f in * ; do
    [ -f "$f" ] && break
    done
echo "File name is: $f"

Output:

user@server: for f in * ; do
>     [ -f "$f" ] && break
>     done
root@blog: echo "File name is: $f"
File name is: book.jpg

To Print all file names, we again, use the continue keyword:

for f in * ; do
    [ -f "$f" ] || continue
    file_name="$file_name $f"
done
echo File Name are: "$file_name"

The above script print all the files founded in the current directory, this is what the code is doing:

  • The * search will return all files
  • [ -f "$f" ] - this ensures we are only processing files, and if it is not a file e.g if it is a directory, the continue keyword would skip the remaining commands inside the body of the enclosing loop for the current iteration and passes program control to the next iteration of the loop.
  • file_name="$file_name $f" - This append the file name to file_name variable, and once we exit the list we print all the complete file names

Output

user@server: for f in * ; do
>     [ -f "$f" ] || continue
>     file_name="$file_name $f"
> done
user@server: echo File Name are: "$file_name"
File Name are:  book.jpg book.txt gg.txt out.txt

While loops and Until loops

We have previously looked at for loop, where we use it in looping through a list of words or items, and now we are unto the while and until loops. The while and until loops is based on whether the condition is true or false.

A while loops while the condition is true and until loops while the condition is false. Consider the following example using while loop:

count=10
    while (( count >= 0 )) ; do
    echo -e "$count \c"
    ((count--)) ; done
echo

The above example counts and print from 10 through 0, it does the following:

  • We set an initial value of where the count should begin, in this case, we want the count to begin at 10; count =10
  • We then say, "while the count is greater and equals to 0, do the next step, for the first run, it is true, we have 10, so...
  • It prints 10
  • count-- reduces the count by 1, you can also write it as count=count - 1

We then repeat it until it gets to zero, this is the output:

user@server:~$ whileloop.sh
10 9 8 7 6 5 4 3 2 1 0

Let's rewrite the code with an until loop:

count =10
    until (( count < 0 )) ; do
    echo -e "$count \c"
    (( count -- )) ; done
echo

The only thing we changed in the above script is the logic, we are saying, until the variable of count is lesser than zero, keep looping.

Related Post(s)

  • sed (Stream Editor) In Bash Scripting

    Another powerful filter in GNU/Linux is sed (stream editor), which is a program for performing basic editing tasks on the output of another program or on a file. sed or stream editor can be used to p

  • awk In Bash Scripting

    Up until now, we have covered a couple of text processing, from sed to grep to sort, and now we have the big daddy, which is awk. awk (which stands for Aho, Weinberger, and Kernighan - its creators)

  • (Bash Script) Automate Multiple Classicpress & Wordpress Installation + [Caching and SSL]

    If you are tired of using control panels (I am because they are mostly not secure, and most comes loaded with bundles of useless stacks), and want to do everything on the server level, I wrote a menu

  • Returning Values From Functions in (Bash)

    In the guide function in bash, we dive deep in bash functions and we also pass some information into the functions to carry out a certain operation. In this guide, I'll show you how to get informatio

  • Functions in Bash

    Functions are modular building blocks for creating powerful and modular scripts, using function makes it easy to isolate a code since it would be in a single building block, which would be isolated f

  • grep (Regular Expression) In Bash Scripting

    We recently discussed filters in bash scripting, in this guide we would look more into the practical usage of using a filter program, and an example of such a program is grep. grep, in its simplest f