Loops: Repeating Actions in Scripts
Repeat commands automatically with for, while and until loops, and control them with break and continue.
Why loops?
A loop repeats a block of commands. Instead of writing a task 100 times, you write it once and tell the shell how many times, or for which items, to repeat it.
The for loop
A for loop goes through a list of items, one at a time.
for fruit in apple banana cherry; do
echo "I like $fruit"
done
On each round the variable fruit holds the next item. do starts the loop body and done ends it. The output is three lines, one for each fruit.
Ranges of numbers
for i in {1..5}; do
echo "Number $i"
done
for i in {0..20..5}; do
echo $i
done
{1..5} expands to 1 2 3 4 5, so the first loop runs 5 times. {0..20..5} adds a step of 5 and gives 0 5 10 15 20.
There is also a counting style borrowed from C:
for ((i = 1; i <= 3; i++)); do
echo "Round $i"
done
Looping over files
Loops become very useful with wildcards.
for file in *.txt; do
echo "Processing $file"
wc -l "$file"
done
The shell expands *.txt into the list of matching files. Quote "$file" so names with spaces still work.
The while loop
A while loop repeats as long as its test is true.
count=1
while [ $count -le 3 ]; do
echo "Count is $count"
count=$((count + 1))
done
$(( )) does arithmetic. The loop must change something that affects the test. If count never changed, the test would stay true forever and the loop would never end.
Reading a file line by line
while read -r line; do
echo "Line: $line"
done < names.txt
read takes one line of input and stores it in line. It fails when there are no more lines, which ends the loop. The < names.txt after done feeds the file into the loop as input. The -r option stops read from treating backslashes as special. This is the standard safe way to process a file line by line.
The until loop
until is the opposite of while. It repeats until its test becomes true, which means it runs while the test is false.
n=1
until [ $n -gt 3 ]; do
echo "n is $n"
n=$((n + 1))
done
break and continue
breakleaves the loop immediately.continueskips the rest of this round and jumps to the next one.
for n in 1 2 3 4 5 6; do
if [ $n -eq 3 ]; then
continue
fi
if [ $n -eq 5 ]; then
break
fi
echo $n
done
This prints 1, 2 and 4. The 3 is skipped by continue, and the loop stops at 5 because of break.
A practical example: batch renaming
Add a prefix to every JPG in a folder:
for img in *.jpg; do
mv "$img" "holiday-$img"
done
Renaming 300 files by hand would take a long time. The loop takes a moment.
Preview before you change files
Before a loop that changes or deletes files, put echo in front of the risky command:
for img in *.jpg; do
echo mv "$img" "holiday-$img"
done
This prints the commands instead of running them. When the output looks right, remove echo.