Loops: Repeating Actions in Scripts

Repeat commands automatically with for, while and until loops, and control them with break and continue.

~7 min Beginner

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

  • break leaves the loop immediately.
  • continue skips 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.

Sign up to track your progress Mark lessons as read and pick up where you left off Sign up free to unlock this practice Your first practice lesson is free to try. Create an account to unlock the rest and save your progress