Exercise: Three Things at Once
Write three tools for a day's work on Bluebird Bakery's website: three-panes.sh sets up the server, the tests rerunning in watch and a free terminal in one tmux session, wait-for-site.sh waits for the site and gives up at a time limit, and resize-all.sh resizes photos four at a time, while a check script tells you what's done.
Freelancer · Unix · Linux · Updated
A day's work on Bluebird Bakery's website means three things at once: the site's server running, its tests rerunning every few seconds, and a terminal to type in. There are twelve vacation photos to shrink for the site, too. You'll write one tool for each lesson of this chapter: three-panes.sh, which sets up all three in one tmux session with one command; wait-for-site.sh, which waits until the site answers and gives up at a time limit; and resize-all.sh, which resizes the photos four at a time. A check script tells you what's 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, and the exercise uses tmux, watch, curl and python3, which runs the practice web server. Check each one with --version (tmux -V for tmux), and install what's missing:
sudo apt install bats tmux curl python3 # Ubuntu, and Ubuntu in WSL
brew install bats-core tmux watch coreutils # 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. Ubuntu already has watch. A Mac already has curl and python3, and coreutils adds timeout.
Get the exercise code
Open this lesson's GitHub repository, github.com/ask-codey/exercise-bash-three-things-at-once, 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 inside Ubuntu, in your Linux home folder, not in a Windows folder under /mnt/c, so chmod works on the scripts.
The folder holds:
answers.txt: five questions, each with a numbered line for your answer.three-panes.sh,wait-for-site.shandresize-all.sh: the three tools. Each starts with a comment that says exactly what it must do and print. These, andanswers.txt, are the files you write.site/,start-server.shandtest.sh: the bakery's website, the script that serves it athttp://localhost:8000after a few seconds, and the tests from Waiting for Things: watch, until and timeout.photos/andresize.sh: twelve pretend photos, a packing list that ended up among them, and the one-second stand-in resizer from Running Work in Parallel: &, wait and xargs -P.check.bats: the check. You don't need to change it.
What to do
Open a terminal in the exercise folder, and make the scripts runnable:
chmod +x *.sh
- Answer the questions in
answers.txt, trying each one in tmux or the terminal first. Write each answer after its), like the example:0) tmux new -s bakery. - Write
three-panes.sh. One session namedbakery, with three panes:./start-server.sh,watch -n 5 ./test.sh, and a free terminal. Run it, watch the tests turn from000to200as the server starts, then detach with Ctrl+Bdand run it again: you're back in the same session, not a new one.tmux kill-session -t bakeryends it. - Write
wait-for-site.sh URL [SECONDS]. With the server stopped,./wait-for-site.sh http://localhost:8000/ 5gives up after five seconds. Start the server in another pane, run it again, and it says the site is up as soon as it answers. - Write
resize-all.sh. Four at a time, every photo and nothing else, and it finishes only when the last photo is done.time ./resize-all.shshows the difference: about three seconds instead of twelve.
Check your work
From the exercise folder:
bats check.bats
At first almost every check fails. That's expected: each question you answer and each tool you finish turns its check into a ✓. The check tries your scripts on copies in a throwaway folder, with a practice website on a free port and a tmux of its own, so it never touches your own sessions or your server. It stops only what it started, and takes about 20 seconds. A failing check says what to look at, for example resize.sh ran one photo at a time, so it took 12 seconds. Run up to 4 at once with xargs -n 1 -P 4.
Hints
- A session from a script:
tmux new -d -s bakery COMMANDmakes it without attaching, with the command in its first pane.tmux split-windowtakes a command too, andtmux has-session -t bakerysucceeds only when the session exists (tmux: Panes, Windows and Sessions). - A default:
${2:-30}is the second argument, or 30 when there isn't one. - Waiting with a limit:
until curl -s -o /dev/null "$url"; do sleep 1; donewaits for an answer, andtimeoutstops it at the limit with exit code 124.timeoutruns one command, so the loop goes insidebash -c '...'(Waiting for Things: watch, until and timeout). - Only the photos, spaces and all:
find -name '*.jpg' -print0andxargs -0keep each name whole,-n 1gives eachresize.shone name, and-P 4runs four at once (Running Work in Parallel: &, wait and xargs -P). - Counting what's done:
small=(small/*)puts every file insmall/into an array (Arrays), and${#small[@]}is how many there are.
When you're done
Every check passes:
check.bats
✓ the scripts, the site and the photos are here
✓ 1) the key that splits a pane side by side
✓ 2) the key that detaches
✓ 3) the command that brings back the session bakery
✓ 4) the command that reruns the tests every 5 seconds
✓ 5) the exit code timeout gives when it stops a command
✓ 6) wait-for-site.sh with no URL shows how to use it
✓ 7) wait-for-site.sh waits for a server that is starting up
✓ 8) wait-for-site.sh gives up after SECONDS
✓ 9) wait-for-site.sh waits 30 seconds when SECONDS isn't given
✓ 10) resize-all.sh resizes every photo and nothing else
✓ 11) resize-all.sh runs four at a time
✓ 12) resize-all.sh waits for every photo, then counts them
✓ 13) three-panes.sh makes the session bakery with three panes
✓ 14) the server runs in one of the panes
✓ 15) the tests rerun every 5 seconds in another pane
✓ 16) three-panes.sh run a second time makes nothing new
17 tests, 0 failures
Now put two of your tools together: which line, added to three-panes.sh after the session is made, would wait until the site answers before attaching, and give up after 30 seconds?
Looking back
This is the last exercise of Bash in Depth. Think back to the first chapter: you typed pwd in a practice folder called trip, and the terminal answered with a path you had to learn to read. Since then you've moved, copied and deleted files without a mouse, searched a month of logs in a second, set up your shell the way you like it, and written scripts that take arguments, make decisions, loop, fail well and report back: a recipe box app, a weather report from a real web service, a backup that runs while you sleep, a deploy over SSH. In this chapter you ran a server, its tests and your own typing side by side, and made a computer do four jobs at once.
That doesn't mean you know every command, and nobody does. It means the terminal is a place you can work, a manual page or --help is something you can read, and a script file is an application you can write whenever a job calls for one. The mouse is still there, and it's still the better tool for plenty of jobs; now you have a choice. A good next step is a job you do by hand every week: write the script for it.
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-three-things-at-once), 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-three-things-at-once.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-three-things-at-once.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-three-things-at-once, 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
A server, its tests and your typing side by side, a wait that knows when to give up, and work spread over every core: that's a developer's terminal, and you built it from scripts you wrote yourself.