Skip to content

Exercise 4 of 10 · Conditions and exit status

Rainwater Tank Gauge

What you will make

A two-line gauge readout for a rainwater tank: one line chosen by an if/elif/else on the level, and one line showing the exit status of a comparison run on its own.

The one new idea: if runs a command and checks its exit status; [ ] compares numbers with -lt/-gt and needs spaces around its brackets

Everything in the shell that decides anything - if, while, && - is really just looking at an exit status, where 0 means success. Once that clicks, if statements stop being magic. And [ is the single most mis-typed thing in bash: it is a command, so it needs spaces, and it uses -lt and -gt because < and > already mean something else.

Go straight to the code ↓

Asking a question

Every command finishes with an exit status: 0 means "it worked" or "yes", anything else means "no" or "it failed". if runs a command and looks at that status — nothing more. The command it usually runs is [, which is a real command (another name for test) that compares things and exits 0 when the comparison holds.

Because [ is a command, its arguments have to be separate words. There must be a space after [ and a space before ]. Write [ "$n" -lt 20] and the shell hands 20] to the command as one word, and it complains that the closing ] is missing.

Numbers are compared with -lt, -le, -gt, -ge, -eq and -ne. The symbols < and > mean something else on a command line — they redirect input and output to files — so [ "$n" < 20 ] tries to read a file called 20.

After the if you can add elif for further tests and else for everything left over, and the whole thing ends with fi. Quote the variable inside the test; if it were ever empty, [ -lt 20 ] would be a broken command.

A worked example

Shell
steps=8200
if [ "$steps" -ge 10000 ]; then
  echo "Goal reached"
elif [ "$steps" -ge 5000 ]; then
  echo "Halfway there"
else
  echo "Keep walking"
fi
[ "$steps" -ge 10000 ]
echo "Goal met? $?"
Output
Halfway there
Goal met? 1

8200 is not at least 10000, so the first branch is skipped; it is at least 5000, so the second one prints. The last two lines run a test on its own and print $?, the exit status of the previous command — 1, meaning the comparison was false.

Your turn

The editor holds a gauge for a rainwater tank. The level is a percentage: under 20 means refill, under 50 means getting low, otherwise plenty. It should print one line from the if, then a second line showing the exit status of a standalone test.

Two lines are marked. One compares with <, which the shell reads as a file redirect. One has no space before its ]. With both bugs in place the script falls through to the wrong branch and prints error messages along the way.

Fix both tests so that a level of 35 reports "getting low".

If something goes wrong

20: No such file or directory means a < is still being used as a comparison. Replace it with -lt.

[: missing ']' means the closing bracket is stuck to the number before it. Put a space between them.

If the if prints the wrong branch with no errors, check which operator you used: -lt is "less than", -gt is "greater than".

Write your code

Runs in your browser. Press Run (or Ctrl/Cmd+Enter) and the output is checked for you.

Ctrl/Cmd+Enter to run

Press Esc then Tab to move keyboard focus out of the code editor.

Ready
Output will appear here after you run your code.

The runtime is starting in the background. You can type now — it will be ready before you are.

The answer appears here once you have run your code at least once.

Things that often go wrong here

Comparing numbers with < or > inside [ ]
On a command line, < and > are redirections, so [ "$level" < 20 ] tries to read a file named 20 rather than compare anything. Use -lt and -gt for numbers.
Leaving out the space before ] or after [
[ is a command and ] must arrive as its own argument. Written as 50], the shell hands over one word and the test command complains that its closing bracket is missing.
Leaving the variable unquoted inside the test
It happens to work when level is set, but if the variable were ever empty the command would become [ -lt 20 ], which is a syntax error. Quoting "$level" keeps the test valid no matter what.
Reading exit status 0 as false
In the shell, 0 means success or true, and any non-zero value means failure or false - the reverse of what many languages use for booleans. That is why the standalone test prints 0 when 35 really is above 30.

Want a blank editor instead? Open the Bash & the Shell playground.