shell-docs is a living documentation source for the portable shell.
It's both documentation and compatibility tests, with each document providing comprehensive support breakdown for shell compatibility.
Check the echo builtin document for an example.
This project is in its infancy. There are some important architectural goals we want:
-
No reliance on anything but portable coretools and portable sh.
We want the tools provided to be self hosting. They test and document the shells, but must also be executable in those shells, estabilishing a simpler dependency model.
This certainly restricts our ability to do things (no fancy markdown parsers from higher languages, etc) but it is necessary.
-
Clear support definitions
The POSIX standard is great for the shell, however, it is not enough as a source of information of what shells in the wild actually support. Some features, such as local, are undefined in the spec but widely available.
We aim to solve that by making a living executable documentation, in the spirit of MDN (Mozilla Developer Network) docs, providing extensive compatibility information.
Currently, the project works by taking documentation written by humans and running the example
code snippets as tests. The pipeline is a single build entrypoint with four stages, all
sharing lib.sh (portable sh + coreutils only):
extract— convert the docs into an on-disk test suite (tests/)run— run the suite against every shell from https://github.com/alganet/shell-versions (results/)collect— fold the TAP results into the durable compatibility ledger (data/), and report what changed versus the previous ledger: recoveries (fail→pass), regressions (pass→fail) and new failures../build changesre-prints the last build's report.render— rewrite each doc's compatibility table from the ledger
Each example runs in a fresh empty directory under a minimal, uniform environment —
env -i HOME=/home/shelldocs PATH=/bin:/usr/bin for every shell — and only the example's
standard output is compared against its Output: block (stderr is discarded). This keeps
results reproducible (no stray files for an unquoted * to match) and consistent across shells.
Two consequences for writing examples:
- Be self-contained. Don't assume a starting directory or environment —
cdto a known path, or setHOME, inside the example if it matters. - Normalize varying diagnostics with familiar shell, not magic. Error wording differs
wildly between shells, so don't try to match it. Test the exit status of a contained
failure instead, e.g.
( readonly_var=x ) 2>/dev/null || echo "it is read-only". To show a diagnostic on purpose, opt into it with an explicit2>&1in the example.
Every document follows the same shape — docs/sh/builtins/echo.md
is the reference:
- the title: the exact feature or command name;
- a one-line
>description of what it does (present tense, active voice); - a usage line, e.g.
- **echo** [-neE] [_string_...], with_placeholders_in italics; - a specification link (POSIX where it applies, otherwise the defining shell);
- one H2 section per behaviour/option, each opening with a one-line explanation before
its
Example:/Output:block; - the generated Compatibility table, left untouched by
render.
The ledger under data/ is the single durable source of truth: one TAB-delimited
feature / shell / version / status record per observation. It is committed to git, so
knowledge accumulates across shell-image refreshes — a version that later disappears from the
image keeps the result we already recorded for it. tests/ and results/ are disposable
scratch and are git-ignored.
Compatibility cells are derived honestly from the sparse set of tested versions:
X+— works fromXonward (a lower tested version was observed to fail, soXis a confirmed introduction point)?X+— works atX, the oldest version we tested; it may well work in older ones we lackX-Y— worked fromXthroughY, then stopped (a newer tested version fails)-unsupported in every tested version ·.no data
Whole workflow (process docs, run tests, regenerate compat tables):
./build all
Individual stages: ./build extract, ./build run, ./build collect, ./build render.
./build clean removes the tests/ and results/ scratch (never data/).
License newly written docs (GFDL):
reuse annotate --copyright "Alexandre Gomes Gaigalas <alganet@gmail.com>" --license GFDL-1.3-or-later --recursive docs
The data/ ledger is licensed ISC (it is factual data produced by the ISC tooling). Each
.results file carries its own # SPDX-... header, written automatically by collect/render,
so it stays REUSE-compliant across regeneration — no reuse annotate step is needed for it. Run
reuse lint to check the whole tree.
Code and the data/ compatibility ledger in this repository are licensed under ISC.
Documentation in this repository is licensed under GFDL-1.3 or later.