Exercise: The Health Check
Finish a health check script for a book club's home server: read the disk, memory, load and failed services, warn about each problem, and exit with a code that says whether all is well, while a check script runs it on a pretend computer, job by job.
Journeyman · Unix · Linux · Updated
The book club's website runs on a small home server in Sam's closet, and Sam doesn't want to sign in every morning to read four gauges. The club wants one command that answers "does the server need a look?": a line each for the disk, the memory, the load and the services, a plain warning for each problem, and an exit code that says whether all is well, so another script can act on it. You'll finish health.sh, and a check script runs it on a pretend computer with a full disk, too little memory or a failed service, job by job.
Before you start
You need a terminal: Ubuntu on Linux or in WSL on Windows, or the Terminal app on a Mac. The check runs with bats, from Testing Scripts with bats. 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. health.sh reads free and systemctl, which Linux and WSL have and a Mac doesn't; on a Mac, the check still works, because it brings its own pretend versions.
Get the exercise code
Open this lesson's GitHub repository, github.com/ask-codey/exercise-bash-the-health-check, 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:
health.sh: the health check to finish, with five jobs, each a comment that says what to read, what to print and when to warn.check.bats: the check. You don't need to change it.
What to do
cd into the exercise folder, run chmod +x health.sh, and open it with nano health.sh. Strict mode, the warnings list and the first line are there already. Each job is a comment, with room under it for your lines:
# 1. Disk. Read how full the main disk is, as a number without the %:
# the fifth column of the second line of df / (tr -d % takes the % off).
# Print: Disk: 42% of / used
# When it's 90 or more, add the warning:
# the disk is 95% full. Find what's filling it with du.
- Disk: the
Use%of/fromdf /, aDisk:line, and a warning at 90% or more. - Memory: the total and available MB from
free -m, aMemory:line, and a warning when less than 10% is available. - Load: the 1-minute load from
uptimeand the CPUs fromnproc, aLoad:line, and a warning when the load's whole number is at least the number of CPUs. - Failed services: their names from
systemctl --failed --no-legend --plain, aFailed services:line with how many, and a warning for each. - The verdict: every warning, each on a line that starts
WARNING:, thenAll OK.and exit code 0, orWarnings:and the number, and exit code 1.
Each job's comment gives its line and its warning word for word. Save, and run ./health.sh as you go.
Check your work
From the exercise folder:
bats check.bats
At first most checks fail. That's expected: each job you finish turns its checks into a ✓. A failing check names its job, says what it expected and shows what your script printed, for example Job 1: with the disk 42% full, health.sh should print the line Disk: 42% of / used. The check makes small stand-ins for df, free, uptime, nproc and systemctl in a throwaway folder of its own, so it can pretend the disk is full without touching yours. Its last check runs health.sh on your own computer.
Hints
- Try each reading at the prompt first.
df / | awk 'NR == 2 {print $5}'should print your disk's percentage; then add| tr -d %and save it with$( )(Values from Commands: $( ) and $(( ))). (( ))compares whole numbers, sofree -m(MB) works andfree -h(3.8Gi) doesn't. For the 10%, multiply first:available * 100 / total.- The load has a dot in it, and
(( ))can't count with that.${load%.*}cuts off the dot and what follows, the way${f%.tmp}cut off an ending in For Every File: for Loops. - Each failed service's name is the first word of its line. Loop over them with
for, counting as you go (Stopping Early: break, continue and Counters). - Job 5 is two parts: a
forloop over"${warnings[@]}", then${#warnings[@]}to chooseAll OK.orWarnings:(Arrays).
When you're done
Every check passes:
check.bats
✓ the script is here and bash can read it
✓ 1) the disk line
✓ 1) a disk 90% full or more gets a warning
✓ 2) the memory line
✓ 2) less than 10% of memory available gets a warning
✓ 3) the load line
✓ 3) a load of one per CPU or more gets a warning
✓ 4) failed services are counted
✓ 4) each failed service gets a warning
✓ 5) a healthy computer: All OK. and exit code 0
✓ 5) two problems: two warnings, Warnings: 2 and exit code 1
✓ it runs on this computer too
12 tests, 0 failures
And on Sam's laptop, ./health.sh then echo $? prints this; yours shows your own computer's numbers:
Health check, 2026-11-09 14:42
Disk: 4% of / used
Memory: 62043 MB available of 64194 MB
Load: 0.42 on 24 CPUs
Failed services: 0
All OK.
0
Want to see a warning on your own computer? Change job 1's 90 to 1 for a moment, run it, and change it back. Then think about the exit code: what could a script that runs health.sh every morning do with a 1 that it couldn't do with the words alone?
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-health-check), 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-health-check.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-health-check.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-health-check, 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
You've built the check an administrator runs first on any server, written so both people and other scripts can read its answer: plain lines for you, and an exit code for the script that runs it on a schedule.