
No major shell dialect beyond the POSIX subset has a definition other than its implementation
Scope
This artifact records the state of shell specification, testing, and reimplementation as of August 2026. It compiles published release notes, project documentation, academic work, and repositories read directly. It contributes no new experiment and proposes nothing.
The central observation is that the absence of a formal definition for the bash dialect is not an omission but a structural consequence of how single-implementation languages evolve, and that every downstream gap recorded below traces to it.
Why the definitional gap exists
The Bourne shell of 1977 was never formally specified; its semantics were what its C implementation did. Bash, released in 1989 as the GNU Project’s reimplementation and extension of Bourne, accreted features over approximately thirty-five years, largely under a single maintainer: arrays, [[ ]], extended globbing, process substitution, coprocesses, associative arrays. Each feature’s semantics, including its interactions with every other feature, were fixed by the implementation at the moment of merge. The manual pages and texinfo manual describe intent, and where documentation and code disagree the code prevails, because deployed scripts depend on actual rather than documented behaviour.
One subset was formalised, and the reason is instructive. POSIX specifies the sh subset because multiple independent implementations needed to interoperate: dash, ash, ksh, busybox sh, and bash in POSIX mode. Bash extensions, zsh extensions, and fish extensions never faced that pressure, because each had exactly one implementation. A single implementation is a complete and precise definition of its own language. It is not a human-readable or independently checkable one.
The pattern is general rather than particular to shells. CPython defined Python, Perl 5 defined Perl, and the Matz interpreter defined Ruby, each for decades. Formalisation work historically begins when a second implementation is attempted.
The reimplementation wave
Second implementations are now being attempted, which is what makes the definitional question live.
| Project | Language | Specification used | Validation |
|---|---|---|---|
| brush | Rust | the bash binary | approximately 1,700 oracle-based compatibility tests |
| Oils / OSH | C++, Python | the bash binary | spec tests across bash, dash, mksh, zsh, OSH |
| fish 4.0 | Rust, from C++ | the previous fish binary | the existing test suite |
| sush | Rust | the bash binary | a maintained fork of the GNU bash repository |
The fish case is the clearest instance of the pattern. The project ported its entire codebase from C++ to Rust incrementally within one repository, using its own prior binary as the oracle. Maintainers rewriting their own shell could not point to a document defining it.
Four consequences
Specification. No conformance suite or formal semantics exists for the full bash dialect. Behaviour in extended features, and particularly in interactions between features, contains emergent cases nobody designed: errexit inside && chains, word splitting against the field separator, quoting inside pattern substitution, arithmetic evaluation order. Formalising it presents a choice between blessing historical accidents as law and declaring some behaviours defects, which would break deployed scripts. Neither maintainers nor users have had an incentive to resolve it, because the question of what bash does is answerable by running bash.
One academic exception exists. Smoosh provides a formal, mechanised semantics for the POSIX shell and demonstrates that mechanised shell semantics are tractable.3 Its scope is POSIX rather than the bash dialect.
Testing. GNU bash ships a regression suite, accumulated as defects were fixed rather than derived from the language surface. No coverage metric against the dialect exists, because the dialect has no enumerable definition to measure against. The state of the art is therefore oracle-based differential testing: identical inputs are run on the implementation under test and on reference shells, and outputs and exit codes are compared.
The brush project’s strategy converts unknown gaps into countable ones: when a missing feature is found, a failing test is committed, so the gap becomes an artifact rather than folklore.1 The Oils spec tests run thousands of cases across five shells and publish per-shell passing counts against bash, which is the closest existing approximation to a quantitative cross-shell compatibility benchmark; brush was added to that testing in 2025.2
The limitation applies to all of them. A pass rate quantifies gaps relative to a finite test set, and no test set covers the dialect. A claim that an implementation is nearly feature complete is not falsifiable, because the denominator does not exist.
Reproducibility. Because semantics reside in the implementation, behaviour varies across bash versions, with documented user-visible incompatibilities between 5.0, 5.1, 5.2, and 5.3; across build configurations; and across the boundary between shell builtins and external utilities, where a builtin test and /bin/test are distinct programs. A script’s behaviour is reproducible only modulo an exact version and environment. Oracle-based testing inherits the problem directly: the specification changes whenever the oracle is upgraded.
Generalizability. Findings about one shell rarely transfer, because each dialect is an independently accreted artifact. Where two shells share a feature the behavioural details differ, which is why the Oils framework encodes per-shell expected outputs. Any method, tool, or formal result built for one dialect must be substantially re-derived for the next.
Rate of change
The intellectual framework of implementation-as-definition, regression-suite testing, and documentation-as-intent has been substantially unchanged since the late 1980s. Three developments constitute the movement since, all within roughly the last decade and concentrated in the last three years: the Oils project’s systematic empirical spec-test methodology, Smoosh’s demonstration that formal POSIX semantics are mechanisable, and the reimplementation wave recorded in table 1.
That wave is the first sustained interoperability pressure applied to the bash dialect specifically. On the historical pattern, interoperability pressure is the condition under which specifications get written.
Open questions
- Coverage of the behavioural surface. Whether grammar-based or coverage-guided differential fuzzing across bash, brush, and OSH could estimate how much of the behavioural surface existing suites exercise, with structural coverage of bash’s C source as a proxy denominator.
- Oracle stability. How differential suites should pin and track oracle versions, and whether a consensus semantics derived from majority agreement across several shells would be a more stable reference than any single binary.
- Mechanised semantics beyond POSIX. What extending Smoosh-style mechanised semantics to the bash extensions would require, and whether the result would be descriptive, blessing accidents, or normative, declaring defects.
- Feature usage in real scripts. Whether corpus studies mining scripts from package repositories could rank features by actual usage, allowing compatibility to be measured against scripts that exist rather than against an unbounded dialect.
- Whether a specification crystallises. Whether the current reimplementation wave generates sufficient pressure to produce a de facto bash specification, potentially out of an existing spec-test corpus.
Project documentation, release notes, and repositories consulted August 2026, with one academic source.