Calculate “1/(40rods/hogshead) → L/100km” from your Zsh prompt
Vincent Bernat
I often need a quick calculation or a unit conversion. Rather than reaching for
a separate tool, a few lines of Zsh configuration turn = into a calculator.
Typing = 660km / (2/3)c * 2 -> ms gives me 6.60457 ms1 without
leaving my terminal, thanks to the Zsh line editor.
The equal alias#
The main idea looks simple: define = as an alias to a calculator command. I
prefer Numbat, a scientific calculator that supports unit conversions.
Qalculate is a close second.2 If neither is available, we fall back to
Zsh’s built-in zcalc module.
As the alias built-in uses = as a separator for name and value, we need to
alter the aliases associative array:
if (( $+commands[numbat] )); then aliases[=]='numbat -e' elif (( $+commands[qalc] )); then aliases[=]='qalc' else autoload -Uz zcalc aliases[=]='zcalc -f -e' fi
With this in place, = 847/11 becomes numbat -e 847/11.
The quoting problem#
The first problem surfaces quickly. Typing = 5 * 3 fails: Zsh expands the *
character as a glob pattern before passing it to the calculator. The same issue
applies to other characters that Zsh treats specially, such as > or |. You
must quote the expression:
$ = '5 * 3' 15
We fix this by hooking into the Zsh line editor to quote the expression before executing it.
Automatic quoting with ZLE#
Zsh calls the accept-line widget when you submit a command. We replace it with
a function that detects the = prefix and quotes the expression:
_vbe_calc_accept() { case $BUFFER in "="*) typeset -g _vbe_calc_expr=$BUFFER # not used yet BUFFER="= ${(q-)${${BUFFER#=}# }}" ;; esac zle .accept-line } zle -N accept-line _vbe_calc_accept
When you type = 5 * 3 and press ↲, _vbe_calc_accept strips the
= prefix, quotes the remainder with the (q-) parameter expansion
flag, and rewrites the buffer to = '5 * 3' before invoking the
original .accept-line widget. As a bonus, you can save a few keystrokes with
=5*3! 🚀
You can now compute math expressions and convert units directly from your shell. Zsh automatically quotes your expressions:
$ = '1 + 2' 3 $ = 'pi/3 + pi |> cos' -0.5 $ = '17 USD -> EUR' 14.7122 € $ = '180*500mg -> g' 90 g $ = '5 gigabytes / (2 minutes + 17 seconds) -> megabits/s' 291.971 Mbit/s $ = 'now() -> tz("Asia/Tokyo")' 2026-03-22 22:00:03 JST (UTC +09), Asia/Tokyo $ = '1 / (40 rods / hogshead) -> L / 100km' 118548 × 0.01 l/km

Storing unquoted history#
As is, Zsh records the quoted expression in history. You must unquote it before submitting it again. Otherwise, the ZLE widget quotes it a second time. Bart Schaefer provided a solution to store the original version:
_vbe_calc_history() { return ${+_vbe_calc_expr} } add-zsh-hook zshaddhistory _vbe_calc_history _vbe_calc_preexec() { (( ${+_vbe_calc_expr} )) && print -s $_vbe_calc_expr unset _vbe_calc_expr return 0 } add-zsh-hook preexec _vbe_calc_preexec
The zshaddhistory hook returns 1 if we are evaluating an expression, telling
Zsh not to record the command. The preexec hook then adds the original,
unquoted command with print -s.
The complete code is available in my zshrc. A common alternative is the
noglob precommand modifier. If you stick with to instead of ->
for unit conversion, it covers 90% of use cases. For a related Zsh line editor
trick, see how I use auto-expanding aliases to fix common typos.
-
This is the fastest a packet can travel back and forth between Paris and Marseille over optical fiber. ↩︎
-
Qalculate is less understanding with units. For example, it parses “Mbps” as megabarn per picosecond: ☢️
↩︎$ numbat -e '5 MB/s -> Mbps' 40 Mbps $ qalc 5 MB/s to Mbps 5 megabytes/second = 0.000005 B/ps