Exercise 3 of 10 · Arithmetic and printf
Moving-Day Boxes
What you will make
A three-line moving-day estimate: rooms, total boxes, and rolls of tape at ten boxes per roll, rounded up, with the numbers lined up in a column.
The one new idea: $((...)) does whole-number arithmetic, and printf formats columns but never adds a newline
Shell scripts count things constantly: files processed, retries left, seconds elapsed. $((...)) is the only arithmetic bash has, and it works on whole numbers only, so knowing how to round up with it is a real skill. printf is how scripts produce tidy, columned output instead of ragged echo lines, and the fact that it does not add a newline catches everyone once.
Go straight to the code ↓Doing sums in the shell
Variables hold text, and $((...)) is how you ask the shell to treat that text as numbers. Inside the double parentheses you can write +, -, *, / and % (the remainder), and you can name variables without the $:
boxes=$((rooms * boxes_per_room))Shell arithmetic is whole numbers only. 7 / 2 is 3, not 3.5 — the fraction is thrown away. That makes "round up" a small puzzle: to divide by 10 and round up, add 9 first, then divide. 24 + 9 is 33, and 33 / 10 is 3, which is the right number of tape rolls for 24 boxes.
printf is the other half of this exercise. Unlike echo, it prints a format string with % slots filled in by the arguments that follow: %d for a whole number, %s for text, %3d for a number padded to width 3, and %-12s for text padded to width 12 and left-aligned. And printf never ends the line for you — if you want a new line, you write \n yourself.
A worked example
eggs=27
per_box=6
full=$((eggs / per_box))
loose=$((eggs % per_box))
printf "%-12s %3d\n" "Eggs:" "$eggs"
printf "%-12s %3d\n" "Full boxes:" "$full"
printf "%-12s %3d\n" "Loose:" "$loose"Eggs: 27
Full boxes: 4
Loose: 327 / 6 gives 4 full boxes and 27 % 6 gives the 3 left over. The %-12s pads each label to 12 characters so the numbers line up, and every format string ends with \n.
Your turn
The editor holds a moving-day estimate: a number of rooms, boxes per room, the total number of boxes, and the rolls of tape needed at ten boxes per roll (rounded up). It should print three aligned lines.
Three lines are marked. One uses $(...) where it needs $((...)) — single parentheses mean "run this as a command", so the shell tries to run a command called rooms. One divides without rounding up, so 24 boxes come out as 2 rolls when 3 are needed. And one printf has lost its \n, so the next line is glued onto its end.
If something goes wrong
rooms: command not found means a sum is still inside $(...) instead of $((...)). Count the parentheses on both sides.
If two lines run together, look for a printf whose format string does not end in \n.
If the tape count is one too low, the division is rounding down. Add 9 before dividing by 10, and keep the extra parentheses so the addition happens first.
Write your code
Runs in your browser. Press Run (or Ctrl/Cmd+Enter) and the output is checked for you.
Press Esc then Tab to move keyboard focus out of the code editor.
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
- Using $(rooms * boxes_per_room) with single parentheses
- $(...) runs what is inside as a command and captures its output. There is no command called rooms, so you get an error and an empty result. Arithmetic needs the double parentheses of $((...)).
- Leaving a printf format string without \n
- echo ends the line for you; printf does not. Without \n the next thing printed lands on the same line, which is why Rooms and Boxes were glued together in the starter.
- Expecting 24 / 10 to give 2.4 or to round to 3
- Shell arithmetic is whole numbers only, and division simply discards the remainder, so 24 / 10 is 2. Rounding up is your job: add one less than the divisor before dividing.
- Writing $(( boxes + 9 / 10 )) without the inner parentheses
- Division binds tighter than addition, so this computes 9 / 10 first, which is 0, and then adds it to boxes. The addition needs its own parentheses: (boxes + 9) / 10.
Want a blank editor instead? Open the Bash & the Shell playground.