Loops

Loop, as the name implied, is execute a set of code repeatedly. Under one clear condition, POSIX shell allows looping to happen. Here, we will look into several types of looping mechanism.

for

For looping mechanism is a single feed known listing loop mechanism. It requires a known list as a feed to its loop.

Pattern

There are various way to write the for loop. However, as a good practice, you write it:

for variable in (list of items); do
        ...
        echo "Use $variable for each elements in the list."
done

Good Practice:

  1. keep to do the same line as for. It is the same as open bracket for most programming languages.
  2. keep done as its own. It is the same as close bracket for most programming languages.


Basic Use

You can feed a list of items after in keyword. For loop will read each of the element and pass it via the variable ($fruit) for you to use.

for fruit in banana papaya pineapple; do
        echo "I love $fruit."
done

NOTE:

  • Avoid applying quote to the list of items. Doing so will turn the whole thing into 1 single string element.


Search Directory

To search files in directory, you can use the wildcard and pattern searches. However, if there is no match (0 element), for loop will print the given pattern as a result. Therefore, you'll need to check the file existence before using the element. Here is an example for searching mp3 files in the current directory:

for file in ./*.mp3; do
        [ -e "$file" ] || continue   # skip this if $file is ./*.mp3, which doesn't make sense.
        ...
done


C-Style Countable Loop

In POSIX shell, this loop is not supported. You need to use seq external command as a work around OR use the while loop.

for i in $(seq 1 5); do
        echo "$i"
done

While

While mechanism is a conditional iteration. It looks for a conditions to run the iterations or it meets its breaking condition (with a break keyword).

Pattern

There are various way to write the while loop. However, as a good practice, you write it:

while [ condition ]; do
        ...
done

NOTE:

  1. keep the do the same line as while. It is the same as open bracket for most programming languages.
  2. keep done as its own. It is the same as close bracket for most programming languages.


Basic Increment/Decrements Use

You can feed a condition to the while loop. The loop will repeat itself until the condition is no longer satisfied.

#!/bin/dash
i=0
while [ $i -le 10 ]; do
        echo "I love repeating work."
        ...
        i=$((i + 1))
done


Infinity Loop and Break Triggers

You can also create an infinity loop with while mechanism. The loop will repeat itself until it hits the breaking condition. To break the loop, we use the keyword break.

#!/bin/dash
i=0
while true; do
        if [ $i -gt 10 ]; then
                break
        fi
        echo "I love repeating work."
        ...
        i=$((i + 1))
done


Input Feeding Loop

While mechanism is also commonly used for reading contents by piping input. Here's is an example:

#!/bin/dash
while read ip name aliases; do
        echo "This is: { $name }; IP: { $ip }; Aliases: { $alias }"
done < /etc/hosts

You need the read command to populate the line into the list of variables you requested. This command splits the line component using the $IFS variable.

Lastly, at the done keyword, use the input pipe to provide a file path you wish to read.


Read File Line By Line

Using the input feeding loop approach, in POSIX shell, we can also read a file line-by-line. Here is an example:

#!/bin/dash
old_IFS="$IFS"
while IFS='' read -r line || [ -n "$line" ]; do
        echo "$line"
        ...
done < "/path/to/file"
IFS="$old_IFS" && unset old_IFS

You need read -r command to read the line as it is without special interpretation like backslash, configure your $IFS to none for avoiding parsing complication and lastly, [ -n "$line" ] to avoid last line being ignored if it it doesn't ends with newline (\n).


Loop Through Characters for String

Looping through characters for string in POSIX shell is doable using string manipulation methods. Basically it requires you to duplicate the string into a temporary variable since the loop consumes the string character by character. Then, we use double string subtraction by:

  1. Get the remainder of the strings (off the first character) - $remainder
  2. Get the first character (taking the string and subtract the remainder) - $char
  3. Set the remainder as the next loop's string - tmp="$remainder"
  4. Update index if available - "$((index + 1))"
#!/bin/dash
string="a quick brown fox jumps over the lazy dog"

tmp="$string"   # will consume via loop
index=0
while [ -n "$tmp" ]; do
        remainder="${tmp#?}"
        char="${tmp%"$remainder"}"

        ...

        index="$((index + 1))"
        tmp="$remainder"
done

Therefore, please keep in mind that it is a resource consuming process. Use it sparingly.

Until

until mechanism is similar to while mechanism except the condition works in an opposite manner. It looks for a conditions to break the iterations or it meets its breaking condition (with a break keyword). The pattern is:

Pattern

There are various way to write the while loop. However, as a good practice, you write it:

until [ condition ]; do
        ...
done

NOTE:

  1. keep to do the same line as until. It is the same as open bracket for most programming languages.
  2. keep done as its own. It is the same as close bracket for most programming languages.

An example:

#!/bin/dash
i=0
until [ $i -ge 10 ]; do
        echo "I love repeating work."
        ...
        i=$((i + 1))
done

Select

select mechanism is not available in POSIX Shell. You need to use the infinite while loop to build one yourself. Example:

echo "override it?"
while true; do
        echo -n "(y)es|(n)o: "
        read ret
        case "$ret" in
        y) 
                # do something
                ;;                                   
        n)
                # do something
                ;; 
        *)
                continue
                ;;
        esac
done

Nested Loop

It is possible to have a loop inside of another loop. However, as a best practice to avoid cyclomatic complexity, you should always makes good use of your 3-tabs warning.

If you need more than 3-tabs, you are likely going to produce a highly complex codes. Try break it down using function or simplify the process.

That's all about looping in POSIX shell. Feel free to move up to the next section.