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:
- keep to
do
the same line asfor
. It is the same as open bracket for most programming languages. - 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:
- keep the
do
the same line aswhile
. It is the same as open bracket for most programming languages. - 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:
- Get the remainder of the strings (off the first character) -
$remainder
- Get the first character (taking the string and subtract the remainder) -
$char
- Set the remainder as the next loop's string -
tmp="$remainder"
- 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:
- keep to
do
the same line asuntil
. It is the same as open bracket for most programming languages. - 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.