{
"$type": "site.standard.document",
"bskyPostRef": {
"cid": "bafyreie2jc5vovthggufv6ej2o5va6povk67ptktp3jwkhvxwgg3hb47tu",
"uri": "at://did:plc:46ti67tc37qcmwp2vaynk6fq/app.bsky.feed.post/3mhocfe2yomt2"
},
"coverImage": {
"$type": "blob",
"ref": {
"$link": "bafkreiges63cfpgrdjjux7quqbosrbo4rgdgz3dvxf6g73cnop7cxkn7me"
},
"mimeType": "image/jpeg",
"size": 69825
},
"path": "/en/blog/2026-zsh-calculator",
"publishedAt": "2026-03-22T19:40:19.898Z",
"site": "https://vincent.bernat.ch",
"tags": [
"1",
"The equal alias",
"The quoting problem",
"Automatic quoting with ZLE",
"Storing unquoted history",
"Numbat",
"Qalculate",
"2",
"(q-) parameter expansion\nflag",
"Bart Schaefer",
"zshrc",
"noglob",
"auto-expanding aliases",
"↩︎"
],
"textContent": "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 ms`1 without leaving my terminal, thanks to the Zsh line editor.\n\n * The equal alias\n * The quoting problem\n * Automatic quoting with ZLE\n * Storing unquoted history\n\n\n\n# The equal alias\n\nThe 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.\n\nAs the `alias` built-in uses `=` as a separator for name and value, we need to alter the `aliases` associative array:\n\n\n if (( $+commands[numbat] )); then\n aliases[=]='numbat -e'\n elif (( $+commands[qalc] )); then\n aliases[=]='qalc'\n else\n autoload -Uz zcalc\n aliases[=]='zcalc -f -e'\n fi\n\n\nWith this in place, `= 847/11` becomes `numbat -e 847/11`.\n\n# The quoting problem\n\nThe 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:\n\n\n $ = '5 * 3'\n 15\n\n\nWe fix this by hooking into the Zsh line editor to **quote the expression** before executing it.\n\n## Automatic quoting with ZLE\n\nZsh calls the `accept-line` widget when you submit a command. We replace it with a function that detects the `=` prefix and quotes the expression:\n\n\n _vbe_calc_accept() {\n case $BUFFER in\n \"=\"*)\n typeset -g _vbe_calc_expr=$BUFFER # not used yet\n BUFFER=\"= ${(q-)${${BUFFER#=}# }}\"\n ;;\n esac\n zle .accept-line\n }\n zle -N accept-line _vbe_calc_accept\n\n\nWhen you type `= 5 * 3` and press `↲`, `_vbe_calc_accept` strips the `=` prefix, quotes the remainder with the (q-) parameter expansion\nflag, 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`! 🚀\n\nYou can now compute math expressions and convert units directly from your shell. Zsh automatically quotes your expressions:\n\n\n $ = '1 + 2'\n 3\n $ = 'pi/3 + pi |> cos'\n -0.5\n $ = '17 USD -> EUR'\n 14.7122 €\n $ = '180*500mg -> g'\n 90 g\n $ = '5 gigabytes / (2 minutes + 17 seconds) -> megabits/s'\n 291.971 Mbit/s\n $ = 'now() -> tz(\"Asia/Tokyo\")'\n 2026-03-22 22:00:03 JST (UTC +09), Asia/Tokyo\n $ = '1 / (40 rods / hogshead) -> L / 100km'\n 118548 × 0.01 l/km\n\n\nThe 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\n\n## Storing unquoted history\n\nAs 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:\n\n\n _vbe_calc_history() {\n return ${+_vbe_calc_expr}\n }\n add-zsh-hook zshaddhistory _vbe_calc_history\n\n _vbe_calc_preexec() {\n (( ${+_vbe_calc_expr} )) && print -s $_vbe_calc_expr\n unset _vbe_calc_expr\n return 0\n }\n add-zsh-hook preexec _vbe_calc_preexec\n\n\nThe `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`.\n\n* * *\n\nThe 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.\n\n* * *\n\n 1. This is the fastest a packet can travel back and forth between Paris and Marseille over optical fiber. ↩︎\n\n 2. Qalculate is less understanding with units. For example, it parses “Mbps” as megabarn per picosecond: ☢️\n\n $ numbat -e '5 MB/s -> Mbps'\n 40 Mbps\n $ qalc 5 MB/s to Mbps\n 5 megabytes/second = 0.000005 B/ps\n\n\n↩︎\n\n\n\n *[ZLE]: Zsh Line Editor",
"title": "Vincent Bernat: Calculate “1/(40rods/hogshead) → L/100km” from your Zsh prompt",
"updatedAt": "2026-03-22T13:37:09.000Z"
}