Skip to content

v0.0.34

Choose a tag to compare

@github-actions github-actions released this 12 Oct 22:21
· 431 commits to main since this release
v0.0.34
7fd1b84

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: parse an autoload stub once per name length, not once per name (#6437)

The language and the interpreter

  • interp: answer an escaped literal pattern without the matcher (#6436)
  • interp: keep a name's boolean attributes in its cell, and remember the last cell found (#6438)
  • interp: keep export, unset and declared-empty in the cell; ask a local line's letters once (#6373, step 2) (#6442)
  • interp: keep the deferred-call machinery off every command's door (#6439)
  • interp: keep the plain store compiling over the attributes moved into the cell (#6446)
  • interp: keep the restores every simple command makes where the compiler inlines them (#6447)
  • interp: print a function's body once per definition for $functions (#6440)
  • interp: print an array literal's element text only when a refusal names it (#6443)
  • interp: record the shell's registered names in a filter a store asks first (#6445)
  • interp: store a bare assignment of a plain value without the full road (#6444)

Full Changelog: v0.0.33...v0.0.34