Loops: for and while

for: do something to each item

A for loop runs its body once for each word in a list. do and done mark the start and end of the body:

for fruit in pear plum mango; do
  echo "I have a $fruit"
done

In the terminal

student@vulntech:~$ for fruit in pear plum mango; do
>   echo "I have a $fruit"
> done
I have a pear
I have a plum
I have a mango

The list is often filenames from a glob. This renames every .txt file to .bak:

mkdir -p loops; cd loops; rm -f *.txt *.bak; touch one.txt two.txt three.txt
for f in *.txt; do
  mv "$f" "$f.bak"
done
ls

In the terminal

student@vulntech:~$ mkdir -p loops; cd loops; rm -f *.txt *.bak; touch one.txt two.txt three.txt
student@vulntech:~/loops$ for f in *.txt; do
>   mv "$f" "$f.bak"
> done
student@vulntech:~/loops$ ls
one.txt.bak  three.txt.bak  two.txt.bak

$(seq 1 5) counts from 1 to 5, handy for numbered work:

for n in $(seq 1 5); do
  echo "Backup number $n"
done

In the terminal

student@vulntech:~$ for n in $(seq 1 5); do
>   echo "Backup number $n"
> done
Backup number 1
Backup number 2
Backup number 3
Backup number 4
Backup number 5

while: keep going while a condition holds

A while loop repeats as long as its test succeeds. Here a counter climbs until it reaches 4. $(( )) does arithmetic:

n=1
while [ "$n" -le 4 ]; do
  echo "n is $n"
  n=$(( n + 1 ))
done

In the terminal

student@vulntech:~$ n=1
student@vulntech:~$ while [ "$n" -le 4 ]; do
>   echo "n is $n"
>   n=$(( n + 1 ))
> done
n is 1
n is 2
n is 3
n is 4

Common mistake: If the body never changes what the test checks, a while loop runs forever. Make sure something moves towards the end, like n=$(( n + 1 )) here. Ctrl+C stops a runaway loop.

Reading a file line by line

A while read loop with input redirected from a file is the right way to go through a file one line at a time:

printf 'Ada\nGrace\nLinus\n' > crew.txt
while read -r name; do
  echo "Crew member: $name"
done < crew.txt

In the terminal

student@vulntech:~$ printf 'Ada\nGrace\nLinus\n' > crew.txt
student@vulntech:~$ while read -r name; do
>   echo "Crew member: $name"
> done < crew.txt
Crew member: Ada
Crew member: Grace
Crew member: Linus

Tip: read -r reads the line exactly; without -r, backslashes are treated specially. Make -r a habit.

break and continue

break leaves the loop early; continue skips to the next turn:

for n in $(seq 1 6); do
  if [ "$n" -eq 3 ]; then continue; fi
  if [ "$n" -eq 5 ]; then break; fi
  echo "$n"
done

In the terminal

student@vulntech:~$ for n in $(seq 1 6); do
>   if [ "$n" -eq 3 ]; then continue; fi
>   if [ "$n" -eq 5 ]; then break; fi
>   echo "$n"
> done
1
2
4

Remember: for x in list; do ... done walks a list. while [ test ]; do ... done repeats while true. while read -r line; do ... done < file reads a file.

HOME LEARN COMMUNITY DASHBOARD
Practice terminal Starting…