Exercise: The Greeting Card Maker
Finish card.sh, a small application that takes a name as its argument, asks for a greeting and a signature, and saves a dated, numbered card, then fix its quoting bug and let a check script run it for real.
Apprentice · Unix · Linux · Updated
Every birthday and every thank-you in your family goes through you, and you're tired of writing the same card by hand. In this exercise you finish card.sh, a greeting card maker that runs like any other application: you give it a name, it asks two questions, and it saves a card named after the person and the date, numbered so you can see how many you've sent. Half the script is written for you, and that half has the number-one bash bug in it. A check script runs your card maker for real, with names and greetings chosen to catch that bug, and tells you which jobs are done.
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-greeting-card-maker, 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. On Windows, put it in your Ubuntu home folder, not under /mnt/c, so chmod works on it.
The folder holds:
card.sh: the card maker, with an example done and six jobs, each a comment saying what to add or fix.check.bats: the check. You don't need to change it.
What to do
Open a terminal in the exercise folder, make the script runnable with chmod +x card.sh, and open it with nano card.sh. Each job is a comment, with room under it for your lines:
# 1. Keep the first argument, the person the card is for, in a variable called name.
- The name: keep the first argument in
name(Arguments: $1, $# and "$@"). - The greeting: ask
Greeting [Happy birthday]:withread -r -p, and makeHappy birthdaythe answer when someone presses Enter (Asking Questions: read). - The signature: ask
Signed by:and keep the answer infrom. - Today's date: keep it, as year-month-day, in
today, with$( )anddate(Values from Commands: $( ) and $(( ))). - The card's number: one more than the number of cards already in the
cardsfolder, worked out with$(( )). - The bug: the lines that write the card are already there, but none of them has quotes. Run
./card.sh "Aunt Rosa"once jobs 1 to 5 work, read what goes wrong, and fix every line (Quotes: Double, Single and None).
Save, then run your card maker after each job and read the card it made:
./card.sh Maya
cat cards/Maya-*.txt
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 names the job and gives a hint, for example ./card.sh "Aunt Rosa" should save cards/Aunt Rosa-2026-11-09.txt, but it printed: ./card.sh: line 34: $file: ambiguous redirect. The check runs a copy of your script in a throwaway folder and types the answers for it, so your own cards folder isn't touched.
Hints
- Nothing is saved, or the card says
Dear ,? Job 1 isn't done yet: the lines in job 6 use$name. - An empty answer is an empty variable, so
${greeting:-Happy birthday}, on the line after theread, fills in the default. - The card's number:
$(ls cards | wc -l)counts the cards already there; put it inside$(( ... + 1 )). - "ambiguous redirect" means the file name after
>or>>fell apart at a space. Double quotes around"$file"keep it whole. - A
*in the greeting turned into file names? That line's$greetingneeds double quotes too:echo "$greeting!".
When you're done
Every check passes:
check.bats
✓ card.sh is here, and bash can read it
✓ 1) the card is for the name given as the first argument
✓ 2) the greeting is asked for, with Happy birthday when the answer is empty
✓ 3) the card is signed with the second answer
✓ 4) the card is named and dated with today's date
✓ 5) every card gets the next number
✓ 6) a name with a space comes out exactly as typed
✓ 6) a greeting with a * and two spaces comes out exactly as typed
✓ 6) it says which card it saved
9 tests, 0 failures
And your card maker works like this, with your own date (here, Enter at the first question, then Sam):
Greeting [Happy birthday]:
Signed by: Sam
Card 1 saved: cards/Maya-2026-11-09.txt
Dear Maya,
Happy birthday!
From Sam
Card 1, made on 2026-11-09
Now make it yours. Put it in your ~/bin folder as card, as in Run It from Anywhere: ~/bin on Your PATH, and it runs from any folder like any other command, saving the cards in a cards folder wherever you run it. What would you ask next: a closing line, or the person's age, worked out with $(( ))?
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-greeting-card-maker), 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-greeting-card-maker.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-greeting-card-maker.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-greeting-card-maker, 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
card.sh is a real application in a few lines of text: it takes input from its arguments and from questions, builds values from other commands, and handles any name you give it, spaces and all.