Module 9 · Shell scripting basics
Loops: for
Repeat a block for every word in a list, every file matching a pattern or every number from seq, and rename a hundred files in one line.
What you will learn
- Write `for … in …; do … done` loops over words, globs and `seq` output.
- Quote the loop variable so file names with spaces survive.
- Use `${var%suffix}` to build new names, and `break`/`continue` to steer the loop.
A for loop takes a list of words and runs a block once per word, with a variable holding the current one. The list can be typed by hand, produced by a wildcard, or generated by a command inside $( ). That is the whole feature, and it covers most of the repetition you will ever need.
~% for color in red green blue; do echo "Color: $color"; done
Color: red
Color: green
Color: blue
~% for i in $(seq 1 3); do touch "report-$i.txt"; done
~% ls
report-1.txt report-2.txt report-3.txt
~% for f in *.txt; do echo "$f has $(wc -l < "$f") lines"; done
report-1.txt has 0 lines
report-2.txt has 0 lines
report-3.txt has 0 lines
On one line the parts are separated by ;: for var in list; do commands; done. In a script you spread them over lines and the semicolons disappear. seq 1 3 prints 1 2 3 on separate lines; seq 0 5 20 counts in steps of five, and seq -w 1 10 pads with zeros so names sort properly: 01, 02, …
Looping over files
for f in *.txt is the idiomatic way to visit files. The shell expands the wildcard before the loop starts, so each file name is one word even when it contains spaces. That safety is lost the moment you write for f in $(ls): the output of ls is split on every space, so my notes.txt becomes two useless words. Use globs, not ls, and quote "$f" wherever you use it.
~% cd lab/l85/pics && ls
holiday 1.jpeg holiday 2.jpeg logo.jpeg
pics% for f in *.jpeg; do mv "$f" "${f%.jpeg}.jpg"; done
pics% ls
holiday 1.jpg holiday 2.jpg logo.jpg
pics% for f in *.png; do echo "found $f"; done
found *.png
${f%.jpeg} is the value of f with the shortest match of .jpeg removed from the end; ${f#prefix} does the same from the front, and ${f##*/} strips everything up to the last slash, like basename. The last command shows a classic surprise: when a pattern matches nothing, the shell leaves it as literal text, so the loop runs once with f equal to *.png. Guard against it with [ -e "$f" ] || continue as the first line of the body.
break and continue
continue skips to the next item; break leaves the loop altogether. Both are commands, so you combine them with tests: [ -d "$f" ] && continue skips directories, and [ "$n" -gt 100 ] && break stops after a limit. The loop as a whole has an exit status too, that of its last command, so you can follow done with && echo finished.
Commands in this lesson
| Command | What it does |
|---|---|
for x in a b c; do …; done | Loop over a list of words. |
for f in *.txt; do …; done | Loop over matching files (safe with spaces). |
for i in $(seq 1 10); do …; done | Count with seq. |
seq -w 1 10 | Zero-padded numbers. |
${f%.jpeg}.jpg | Swap a suffix. |
break / continue | Leave the loop / skip to next item. |
Quiz
How many times does `for x in one two three; do echo hi; done` print `hi`?
- Once
- Three times
- Forever
Why is `for f in *.txt` preferred over `for f in $(ls *.txt)`?
- It is faster to type
- File names with spaces stay whole; `ls` output gets split on spaces
- `ls` cannot take wildcards
`f=photo.jpeg`. What is `${f%.jpeg}.jpg`?
- `photo.jpg`
- `photo.jpeg.jpg`
- `.jpg`
What does `continue` do inside a loop?
- Leaves the loop
- Skips the rest of the body and goes to the next item
- Restarts the loop from the first item
Practice
Using a `for` loop with `seq`, create the empty files `report-1.txt` through `report-5.txt` inside `/root/lab/l85/out`.
Write an executable script `/root/lab/l85/sizes.sh` that loops over `/root/lab/l85/data/*.txt` and prints one line per file in the form `name.txt: N` where N is its line count (e.g. `a.txt: 2`).
Rename every `.jpeg` file in `/root/lab/l85/pics` to `.jpg` with a `for` loop (one of the names contains a space, so quote your variables).
Open this lesson in the app to do the tasks in a real Linux machine and have them checked.