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"
doneIn 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
lsIn 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"
doneIn 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 ))
doneIn 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
whileloop runs forever. Make sure something moves towards the end, liken=$(( 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.txtIn 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 -rreads the line exactly; without-r, backslashes are treated specially. Make-ra 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"
doneIn 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 ... donewalks a list.while [ test ]; do ... donerepeats while true.while read -r line; do ... done < filereads a file.