Exercise: The Smart Backup
Finish a backup script that copies a file only when it's needed, with a usage line, --help, --list and a --pick menu, checks for missing, empty and folder names, and a clear message when a copy fails, while a check script runs it job by job.
Apprentice · Unix · Linux · Updated
Your school work deserves a backup, and the one you'll finish here is smart about it. It copies a file only when the file changed since its last copy, never lets an empty file replace a good one, refuses folders and typos with a clear message, and says plainly when a copy fails. It also behaves like a real application: run it wrong and it explains itself, --help shows how to use it, --list shows what's backed up, and --pick offers a numbered menu. A check script runs your script for real, job by job, and tells you which ones work.
Before you start
You need a terminal: the Terminal app on a Mac, or Ubuntu on Linux or in WSL on Windows. The check runs with bats, a small program that runs checks written for bash. If bats --version doesn't print a version number yet, install it:
sudo apt install bats # Ubuntu, and Ubuntu in WSL
brew install bats-core # a Mac, with Homebrew
sudo asks for your password, the one you use to sign in; nothing shows on the screen while you type it.
Get the exercise code
Open this lesson's GitHub repository, github.com/ask-codey/exercise-bash-the-smart-backup, also linked from the Exercise box beside the lesson. Click Code, then Download ZIP, and unzip it into your terminal-practice folder. Or clone it with Git.
The folder holds:
smart-backup.sh: the script to finish, with its usage line and nine jobs, each a comment saying what to do and exactly what to print.essay.txt,notes.txt,reading-list.txt,empty-draft.txtand aphotosfolder: practice files to try it on.check.bats: the check. You don't need to change it.
What to do
Open a terminal and cd into the exercise folder. Let the script run with chmod +x smart-backup.sh (Your First Script), then open it with nano smart-backup.sh. Each job is a comment, with room under it for your lines:
usage="Usage: ./smart-backup.sh FILE, or --list, --pick or --help"
# 1. Run with nothing after its name: print the usage line, and stop with
# exit code 1.
- Run with nothing: print the usage line and stop with exit code 1 (If This, Then That: if and [[ ]]).
--helpor-h: print the usage line and stop with exit code 0.--list: list what's inbackup/, or sayNo backups yet.when there's no backup folder.--pick: a numbered menu of the.txtfiles that asksBack up which file?and backs up the one picked (Menus and Choices: case and select). Jobs 2 to 4 fit in onecase.- A missing file: say so and stop with exit code 1 (Checking Before You Act: File Tests).
- A folder: say so and stop with exit code 1.
- An empty file: keep the last good copy, and stop with exit code 0.
- Copy only when needed: go on when the file is newer than its copy in
backup/, or has no copy yet; otherwise say it hasn't changed. - Say whether the copy worked:
Copied FILE., or the failure message and exit code 1.
Each job's comment gives its message word for word. Save, and try your script on the practice files as you go: ./smart-backup.sh essay.txt twice, then ./smart-backup.sh --list.
Check your work
From the exercise folder:
bats check.bats
At first most checks fail. That's expected: each job you finish turns its check into a ✓. A failing check says what it expected, what your script printed, and what to look at, for example With no backup folder, --list should say so. Test for the folder with -d. The check runs your script in a throwaway folder of its own, with made-up files, so your practice files and your backup/ folder are never touched.
Hints
- Work from the top. Job 1 comes first, so a script run with nothing stops before it tries anything else.
- Spaces inside the brackets:
[[ $# -lt 1 ]], never[[$# -lt 1]]. Numbers compare with-lt, text with==. - One
casefor jobs 2 to 4. A choice can match two patterns,-h|--help); end each choice with;;and the whole thing withesac. A name that matches no choice goes straight on to job 5. - The menu's question is
PS3, set beforeselect. Name the menu's variablefile, and end the menu withbreakso the script carries on. - Turn a test around with
!:[[ ! -s "$file" ]]passes when the file is empty. And-ntcounts a missing copy as older, so one test covers "changed" and "never backed up". - Put
cpitself afteriffor job 9, and quote every"$file": the check tries a name with a space in it.
When you're done
Every check passes:
check.bats
✓ the script is here and bash can read it
✓ 1) run with nothing, it prints the usage line and stops
✓ 2) --help and -h print the usage line
✓ 3) --list says when there are no backups yet
✓ 3) --list shows what's in backup/
✓ 4) --pick shows a menu and backs up the file picked
✓ 5) a missing file: it says so and stops
✓ 6) a folder: it says so and stops
✓ 7) an empty file: it keeps the last good copy
✓ 8) a file with no copy yet is copied, and backup/ is made
✓ 8) a name with spaces works
✓ 8) an unchanged file isn't copied again
✓ 8) a changed file is copied again
✓ 9) a copy that fails says so
14 tests, 0 failures
And by hand, in the exercise folder, the script answers like this (with 3 typed at the menu):
$ ./smart-backup.sh
Usage: ./smart-backup.sh FILE, or --list, --pick or --help
$ ./smart-backup.sh essay.txt
Copied essay.txt.
$ ./smart-backup.sh essay.txt
essay.txt hasn't changed since the last backup.
$ ./smart-backup.sh --pick
1) empty-draft.txt 3) notes.txt
2) essay.txt 4) reading-list.txt
Back up which file? 3
Copied notes.txt.
Now look back at your job 8. A friend asks for a backup that copies every file every time, "to be safe". What would that cost on a folder of 2,000 photos, and what could it do to a good copy?
Submit your exercise
The exercise is how you finish this lesson. When your work is done, put it on GitHub in a repo of your own, then submit that repo's address here.
On GitHub, click +, then New repository. Name it (for example
exercise-bash-the-smart-backup), choose Public, and click Create repository. Leave out the README: your folder already has one.In a terminal in your exercise folder (Ubuntu in WSL on Windows), send your work to your new repo. If you downloaded the ZIP:
git init -b main git add . git commit -m "My solution" git remote add origin https://github.com/your-name/exercise-bash-the-smart-backup.git git push -u origin mainIf you cloned the starter instead, point your copy at your own repo, then commit and push:
git remote set-url origin https://github.com/your-name/exercise-bash-the-smart-backup.git git add . git commit -m "My solution" git push -u origin mainPaste your repo's address, such as
https://github.com/your-name/exercise-bash-the-smart-backup, into Submit your exercise on this page. The site checks that the repo is public and holds your commits, and then the lesson is complete.
Changed something after you submitted? Push it, and submit again: your latest submission is the one that counts.
So what
Your script now makes decisions the way real backup tools do: it checks before it acts, copies only what changed, and tells the person running it what happened. That's the difference between a list of commands and an application.