while and until Loops
The folder list for backup.sh is growing, and you would rather keep it in a file, folders.txt, than inside the script. You do not know how many lines it has. A for loop walks a list you already have; a while loop keeps going as long as something is true, such as "there is another line to read".
The rule
whilerepeats as long as its test succeeds;until, as long as it fails.
Like if, the test is a command, and "succeeds" means exit status 0:
n=3
while (( n > 0 )); do
echo "$n"
(( n-- ))
done # prints 3, 2, 1
until ping -c 1 backup-server >/dev/null; do
sleep 5 # keep trying until the server answers
doneThe most important while in Bash reads a file line by line:
count=0
while IFS= read -r line; do
tar -czf "$dest/$count.tar.gz" "$line" && (( count++ ))
done < folders.txt
echo "Archived $count folders"readsucceeds while there is a line and fails at the end of the file, which ends the loop.IFS=stopsreadtrimming spaces at the ends;-rkeeps backslashes.< folders.txtafterdonefeeds the file to the whole loop.
while true; do ... done loops forever, until something inside uses break or exit.
The shortcut
Check the test before every pass, including the first. Say it aloud: test, body, test, body, ... test fails, stop. For until, flip it: stop when the test succeeds. And make sure something in the body changes the test, or the loop never ends.
Test before pass 1
Is 0 at least 3? No, the test fails, so
untilruns the body.nbecomes 1.
A classic trap: cat folders.txt piped into while read .... The pipe runs the loop in a subshell, so count is back to 0 after done. Redirect with done < file instead.
Drills
Answer fast — trust the rule.
What does n=3; while (( n > 0 )); do echo -n "$n "; (( n-- )); done print?
With n=0, how many times does the body of until [[ $n -ge 3 ]]; do (( n++ )); done run?
Where does the file go in a while read loop that reads folders.txt?
Why write IFS= read -r line rather than plain read line?
Remember
whilerepeats as long as its test succeeds;until, as long as it fails.- The test runs before every pass; something in the body must change it.
- Read files with
while IFS= read -r line; do ...; done < file, not through a pipe.
