Skip to content

v0.0.33

Choose a tag to compare

@github-actions github-actions released this 12 Oct 19:13
· 442 commits to main since this release
v0.0.33
9d58b24

A shell parser, interpreter and formatter in Go. One core — the common
denominator of real shells — with bash, zsh, ksh, dash and ash
as presets over a semantics vector rather than forks of one another.

This is a 0.x tag and means it. Early, and breakage is expected.
What changed since the last tag is the changelog below.

What is in the archive

Seven binaries: the substrate driver sh, the five dialect binaries, and
shfmt, which lays a script out in whichever dialect it was written in.
They install into libexec and never into a bin directory — the
shells are named after the shells they model, so a directory ahead of
/bin on PATH would answer for every program on the machine that
resolves a shell by name, and shfmt stays out of bin so that
homebrew-core's formula of the same name can still be installed.

brew install blairham/tap/sh

Where it stands

Each dialect graded against the real shell it models, over 4488 cases —
make conformance-dialects:

binary against exact behavioral
bash bash 5.3.20 98% 100%
zsh zsh 5.9.2 98% 99%
dash dash 98% 100%
ksh ksh93 AJM 93u+ 96% 99%
ash BusyBox 1.37 ash 93% 99%

Exact is byte-identical stdout, stderr and status; behavioral allows a
diagnostic to be worded differently when the status and output agree. The
distance between the two columns is almost entirely message wording.

What is not there yet

The README's "What is not there yet" is the current list, and it is real.
The sandbox contains the shell rather than the process tree, and there is
no Windows target. Diagnostic wording is a long tail rather than the
headline gap: a bounded sample of twenty everyday error situations — a
missing command, cd onto a file, a redirect that cannot open, an unknown
option to six builtins — comes back byte-identical on standard error and
exit status for every one of the five, 100 of 100 on 2026-09-27. The probe
is internal/cmd/diagsample and takes a few seconds a column, so this line
can be re-measured rather than inherited.

Changelog

Dialects

  • zsh, interp: read autoloaded function files through the parse cache too (#6434)

The language and the interpreter

  • driver, interp: keep the trees of sourced and startup files in a parse cache of our own (#6432)
  • interp: read an arithmetic text's spans once per grammar, and keep dialect moves (#6431)
  • interp: read anchors and complete classes in the substitution reach analysis (#6428)
  • interp: read arithmetic again only when the order in force is not the one it was read in (#6429)
  • interp: settle a default's test on an element from the subscript already read (#6430)
  • interp: start a body's process-group placeholder ahead of the body that asks (#6421)
  • interp: the scope lints say nothing about the engine's own state (#6426)

Full Changelog: v0.0.32...v0.0.33