The Scripts Every Project Has
Most software projects keep a few small scripts for the same four jobs: setup.sh gets a computer ready, dev.sh runs the project while you work, test.sh checks it and deploy.sh puts it live, and setup.sh checks its tools first with command -v.
Journeyman · Unix · Linux · Updated
After this lesson you can
- Give a project setup.sh, dev.sh, test.sh and deploy.sh, and choose the right one for a job
- Check that a script's tools are installed with command -v, and list every missing one before stopping
Key takeaways
- A project's scripts turn the README's steps into commands: setup.sh, dev.sh, test.sh and deploy.sh, kept together in a scripts folder.
- command -v tool prints where a command is, or prints nothing and gives exit code 1 when it isn't installed.
- A setup script checks every tool and lists all the missing ones, so you can install them in one go.
- A test script answers with its exit code: 0 when everything passed, 1 when something didn't, so other scripts can act on it.
- cd "$(dirname "${BASH_SOURCE[0]}")/.." takes a script to the project's top folder, so it works from any folder.
You offer to help with a friend's bakery website, and its README says: install Python and rsync, start a web server with this long command, open this address, and before you put anything live, check every page by hand. Ten steps, and by step six you aren't sure you did step four. Or it's your own project, and after three months you can't remember how you started it. Most software projects solve this the same way: a few small scripts with names everyone recognizes. You run ./scripts/setup.sh once, and ./scripts/dev.sh every time you sit down to work. Web developers find them in almost every project they join. In a JavaScript project the same jobs are often npm run dev and npm test, and underneath, those run shell commands too.
Four jobs, four scripts
Each project script does one job, and the names say which:
setup.shgets a computer ready to work on the project: it checks that the tools are installed and makes any folders or settings files. You run it once, after you download the project.dev.shruns the project on your own computer while you work on it. For a website, that's a small web server, a program that hands pages to your browser, athttp://localhost:8000.test.shchecks the project and answers yes or no.deploy.shputs it live, like the one in Running Commands on Another Computer.
They live together in a scripts folder, and each one starts with the same line:
cd "$(dirname "${BASH_SOURCE[0]}")/.." || exit
Read it as "go to the folder above the one this script is saved in", which is the project's top folder (One Script, Several Files: source taught dirname and BASH_SOURCE). The script then finds site/ and its other files wherever you run it from.
The bakery's project
These lines make the practice project: three pages, one of them missing its <title>, the line that names a page in the browser's tab. They're safe to run again.
mkdir -p ~/terminal-practice/bakery-site/site ~/terminal-practice/bakery-site/scripts && cd ~/terminal-practice/bakery-site
printf '<title>Bluebird Bakery</title>\n<h1>Bluebird Bakery</h1>\n<p>Fresh bread every morning.</p>\n' > site/index.html
printf '<title>Menu</title>\n<h1>Menu</h1>\n<p>Sourdough loaf: $6. Blueberry muffin: $3.</p>\n' > site/menu.html
printf '<h1>Order a cake</h1>\n<p>Call us two days ahead.</p>\n' > site/order.html
ls site
index.html menu.html order.html
Make scripts/dev.sh with nano (Your First Script). Python comes with a small web server, so dev.sh starts it on the site folder:
#!/usr/bin/env bash
# dev.sh: shows the site at http://localhost:8000 until you press Ctrl+C.
cd "$(dirname "${BASH_SOURCE[0]}")/.." || exit
echo "Open http://localhost:8000 in your browser. Ctrl+C stops it."
python3 -m http.server 8000 --directory site
Run chmod +x scripts/dev.sh, then ./scripts/dev.sh, and open the address. When you press Ctrl+C, the server stops:
Open http://localhost:8000 in your browser. Ctrl+C stops it.
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
Keyboard interrupt received, exiting.
setup.sh: check the tools first
dev.sh needs python3, and deploy.sh will need rsync. A script that finds out halfway through that a tool is missing leaves a job half done, so setup.sh asks first. command -v (read "command, dash v") is built into bash. Give it a name, and it prints where that command is, or prints nothing and gives exit code 1:
command -v python3
command -v shellcheck; echo "exit code: $?"
/usr/bin/python3
exit code: 1
ShellCheck (ShellCheck: A Second Pair of Eyes) isn't installed on this computer. If it is on yours, you'll see its path instead. Here's scripts/setup.sh. It checks every tool before it stops, so one run lists everything you need:
#!/usr/bin/env bash
# setup.sh: checks that the tools this project needs are installed.
cd "$(dirname "${BASH_SOURCE[0]}")/.." || exit
missing=0
for tool in python3 rsync shellcheck; do
if command -v "$tool" > /dev/null; then
echo "found: $tool"
else
echo "missing: $tool"
missing=1
fi
done
if [[ $missing -eq 1 ]]; then
echo "Install what's missing, then run setup.sh again."
exit 1
fi
echo "Ready. Start the site with ./scripts/dev.sh"
> /dev/null throws away the path, because only the exit code matters here. Run it:
chmod +x scripts/setup.sh
./scripts/setup.sh; echo "exit code: $?"
found: python3
found: rsync
missing: shellcheck
Install what's missing, then run setup.sh again.
exit code: 1
Why not which? It's a separate program that isn't on every system, while command -v is part of the shell, on Linux and on a Mac alike.
test.sh: answer with an exit code
test.sh checks what you'd otherwise check by hand: here, that every page has a <title>. Its exit code is the answer, so other scripts, and Git in Git Hooks: Scripts Git Runs for You, can act on it:
#!/usr/bin/env bash
# test.sh: checks that every page has a <title>.
cd "$(dirname "${BASH_SOURCE[0]}")/.." || exit
failed=0
for page in site/*.html; do
if ! grep -q '<title>' "$page"; then
echo "No title: $page"
failed=1
fi
done
if [[ $failed -eq 0 ]]; then
echo "Every page has a title."
fi
exit "$failed"
Make it runnable, then run it from inside site, to see the first line at work:
chmod +x scripts/test.sh
cd site
../scripts/test.sh; echo "exit code: $?"
No title: site/order.html
exit code: 1
Go back up with cd .., give the order page a title, and run it again:
printf '<title>Order a cake</title>\n<h1>Order a cake</h1>\n<p>Call us two days ahead.</p>\n' > site/order.html
./scripts/test.sh; echo "exit code: $?"
Every page has a title.
exit code: 0
Check yourself
2 questions to try on the lesson page.
So what
Four scripts with names everyone knows turn a page of instructions into four commands, and a project you can set up, run, check and ship the same way every time.