Skip to content

Exercise 7 of 10 · Functions

Bakery Oven Times

What you will make

A three-line bakery timetable saying when the sourdough, baguettes and croissants come out of the oven, each time computed by one small function and printed as HH:MM.

The one new idea: Functions take $1 $2 arguments, print their result, and are captured with $(...)

Once a script does the same thing in three places, it wants a function. Shell functions look simple but have their own rules: they must be defined before they are called, they receive arguments as $1 $2 $3, and they hand results back by printing them, which you catch with $(...). Clock arithmetic with / and % is also a pattern you will use for durations and timestamps again and again.

Go straight to the code ↓

Naming a block of commands

A function is a named block of commands you can run as if it were a command of its own:

Shell
label() {
  printf "tray-%02d\n" "$1"
}

Inside the function, $1 is the first word given after its name, $2 the second, and so on — the same way a command receives its arguments. local makes a variable belong to the function alone, so it cannot clash with a variable of the same name outside.

Two rules trip people up. First, the shell reads a script top to bottom, so a function must be defined above the first line that calls it; called too early, it is just command not found. Second, a function hands its result back by printing it. To catch that printed text in a variable you wrap the call in $(...): first=$(label 3). Write first=label 3 instead and the shell reads that as "set first to the word label, then run a command called 3".

The %02d in the format string means "a whole number, at least two digits wide, padded with zeros" — which is how 7 becomes 07 on a clock.

A worked example

Shell
label() {
  local n=$1
  printf "tray-%02d\n" "$n"
}
first=$(label 3)
echo "First tray is $first"
label 12
Output
First tray is tray-03
tray-12

The first call is captured with $(...) and used inside a sentence. The second call is not captured, so its output goes straight to the screen.

Your turn

The editor holds a bakery oven timetable. A function done_at takes an hour, a minute and a bake time in minutes, works out when the batch comes out, and prints it as HH:MM. Three batches are listed.

Three lines are marked. The first call sits above the function definition, so at that point the function does not exist yet — move the call (and its echo) below the definition. The printf uses %d instead of %02d, so 9:10 loses its leading zero. And one capture is written without $(...), so out is set to the wrong thing and the shell tries to run 7 as a command.

Fix all three so every batch prints a proper HH:MM time.

If something goes wrong

done_at: command not found means the function is being called before it is defined. Definition first, calls after.

7: command not found means a call is missing its $(...). The whole call — name and arguments — goes inside the parentheses.

If a time prints as 9:10 rather than 09:10, the format string is still %d. Use %02d for both the hour and the minute.

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

Calling a function above its definition
The shell reads a script from top to bottom and only learns a function's name when it reaches the definition. A call above that point is just an unknown command.
Writing out=done_at 7 15 25 without $(...)
This is an assignment prefix followed by a command: out is set to the text done_at just for the duration of a command called 7, which does not exist. Nothing useful is captured. Capturing output always needs $(...).
Using %d where a leading zero is needed
%d prints 9 as 9. Times need two digits in each slot, and %02d is the format that pads with zeros to width 2, turning 9 into 09.
Passing hours like 08 or 09 into arithmetic
In bash arithmetic a number that starts with 0 is read as octal, and 08 and 09 are not valid octal, so the function would fail. This script passes 8, not 08, and that is deliberate.

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