Calculate “1/(40rods/hogshead) to L/100km” from your Zsh prompt

Vincent Bernat

I of­ten need a quick cal­cu­la­tion or a unit con­ver­sion. Rather than reach­ing for a sep­a­rate tool, a few lines of Zsh con­fig­u­ra­tion turn = into a cal­cu­la­tor. Typ­ing = 660km / (2/3)c * 2 -> ms gives me 6.60457 ms1 with­out leav­ing my ter­mi­nal, thanks to the Zsh line editor.

The equal alias#

The main idea looks sim­ple: de­fine = as an alias to a cal­cu­la­tor com­mand. I pre­fer Num­bat, a sci­en­tific cal­cu­la­tor that sup­ports unit con­ver­sions. Qal­cu­late is a close sec­ond.2 If nei­ther is avail­able, we fall back to Zsh’s built-in zcalc module.

As the alias built-in uses = as a sep­a­ra­tor for name and value, we need to al­ter the aliases as­so­cia­tive 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 be­comes numbat -e 847/11.

The quoting problem#

The first prob­lem sur­faces quickly. Typ­ing = 5 * 3 fails: Zsh ex­pands the * char­ac­ter as a glob pat­tern be­fore pass­ing it to the cal­cu­la­tor. The same is­sue ap­plies to other char­ac­ters that Zsh treats spe­cially, such as > or |. You must quote the ex­pression:

$ = '5 * 3'
15

We fix this by hook­ing into the Zsh line ed­i­tor to quote the ex­pres­sion be­fore ex­e­cuting it.

Automatic quoting with ZLE#

Zsh calls the line-finish wid­get be­fore sub­mit­ting a com­mand. We hook a func­tion that de­tects the = pre­fix and quotes the ex­pression:

_vbe_calc_quote() {
  case $BUFFER in
    "="*)
      typeset -g _vbe_calc_expr=$BUFFER # not used yet
      BUFFER="= ${(q-)${${BUFFER#=}# }}"
      ;;
  esac
}
add-zle-hook-widget line-finish _vbe_calc_quote

When you type = 5 * 3 and press ↲, _vbe_​calc_​quote strips the = pre­fix, quotes the re­main­der with the (q-) pa­ram­e­ter ex­pan­sion flag, and rewrites the buf­fer to = '5 * 3' be­fore Zsh sub­mits the com­mand. As a bonus, you can save a few key­strokes with =5*3! 🚀

You can now com­pute math ex­pres­sions and con­vert units di­rectly from your shell. Zsh au­to­mat­i­cally quotes your ex­pressions:

$ = '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
“That's the way I like it!” says Grampa
Simpson
The metric system is the tool of the devil! My car gets forty rods to the hogshead, and that’s the way I like it! ― Grampa Simpson, A Star Is Burns

Storing unquoted history#

As is, Zsh records the quoted ex­pres­sion in his­tory. You must un­quote it be­fore sub­mit­ting it again. Oth­er­wise, the ZLE wid­get quotes it a sec­ond time. Bart Schae­fer pro­vided a so­lu­tion to store the orig­i­nal 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 re­turns 1 if we are eval­u­at­ing an ex­pres­sion, telling Zsh not to record the com­mand. The preexec hook then adds the orig­i­nal, un­quoted com­mand with print -s.


The com­plete code is avail­able in my zshrc. A com­mon al­ter­na­tive is the noglob pre­com­mand mod­i­fier. If you stick with to in­stead of -> for unit con­ver­sion, it cov­ers 90% of use cases. For a re­lated Zsh line ed­i­tor trick, see how I use auto-ex­pand­ing aliases to fix com­mon typos.