Skip to content

Bash Compatibility & Scripting

hellish runs real scripts โ€” not a toy REPL. Status legend: โœ… shipped ยท ๐Ÿšง in progress ยท ๐Ÿ“‹ planned

The proof: it built a Linux distribution

The strongest compatibility claim a shell can make isn't a feature list โ€” it's running real-world scripts. hellish was used as the build-script interpreter for a complete Linux From Scratch build: it drove ./configure, autotools, gcc/glibc/binutils, and ~80 packages, producing a disk image that boots (uname -r โ†’ 6.6.32-dlesieur, network up, SysVinit, clean shutdown).

Most "write your own shell" projects die on the first ./configure. hellish built an OS. That's the bar this page documents against.


Feature matrix vs. bash

Area Feature Status
Control flow if/for/while/until/case, functions, &&/\|\|/;/! โœ…
Pipelines/redir pipes, >/>>/<, 2>&1, fd dup, subshells ( ), groups { } โœ…
Conditionals [[ โ€ฆ ]] (string/int/file/unary, &&/\|\|/!/( ), ==), [ ]/test โœ…
Substitution $(โ€ฆ) / backticks, arithmetic $(( โ€ฆ )) (++ -- += ?: , , bases) โœ…
Heredocs <<, <<-, quoted & unquoted, ${VAR} bodies, consecutive, in-function โœ…
Process subst. <(โ€ฆ), >(โ€ฆ), exec > >(tee โ€ฆ) โœ…
Parameter exp. ${v:-w} ${v:+w} ${#v} ${v#p} ${v%p}, substring, positional โœ…
Brace expansion {a,b,c}, {1..n}, with $var/quoted prefixes โœ…
Globbing * ? [โ€ฆ], sorted matches, no-match passthrough โœ…
Options/traps set -o pipefail, set -e/-u/-o, trap (EXIT/INT/TERM) โœ…
Job control bg/fg/jobs/kill, &, dirstack pushd/popd/dirs โœ…
Builtins getopts, read, printf, alias/unalias, hash, type, local, โ€ฆ โœ…
Arrays indexed a=(โ€ฆ), a[i]=v, ${a[i]}, ${a[@]}, declare -a ๐Ÿšง (Wk2โ€“3)
set -e abort on failure in a multi-stage pipeline (simple commands โœ…) ๐Ÿšง (Wk3)
Assoc. arrays declare -A, string keys ๐Ÿ“‹
Pattern/misc ${v/pat/repl}, var+=value (string append), extglob, [[ str == glob ]] RHS, mapfile/namerefs/coproc ๐Ÿ“‹

Honesty matters more than a green wall: the ๐Ÿšง/๐Ÿ“‹ rows are real gaps, tracked openly. The โœ… rows are each locked behind a regression test that's diffed against bash --posix.


Arrays โ€” the headline of this cycle ๐Ÿšง

Arrays are the single biggest compatibility unlock (a huge fraction of real bash scripts use them), and they're the current focus.

# Target syntax (Weeks 2โ€“3)
declare -a fruits=(apple banana cherry)
fruits[3]=date
echo "${fruits[@]}"      # apple banana cherry date
echo "${#fruits[@]}"     # 4
echo "${!fruits[@]}"     # 0 1 2 3
fruits+=(elderberry)     # append
for f in "${fruits[@]}"; do echo "$f"; done   # one field per element, even quoted

Why it's a real change, not a bolt-on: hellish's variable store is currently one string per name. Arrays add a value-vector + type tag to that store, subscript parsing on the assignment side, and @/* handling (with the subtle ""${a[@]}" splits to one field per element even when quoted" rule) on the expansion side โ€” plus a declare/typeset builtin. The good news: bash arrays are shell-local (never exported to the environment), so the execve path is untouched.


How compatibility is guaranteed

Every fixed construct gets a permanent regression case, and a conformance gate diffs each construct under hellish vs. bash --posix:

make -C vendor/42sh test          # the full suite (2481+ cases, growing)
HELLISH=โ€ฆ bash scripts/conformance.sh   # construct-by-construct vs bash โ†’ 0 divergences

If a behavior diverges from bash, it shows up here before it ships. That's the contract behind every โœ… above.


See also: Interactive Experience ยท Performance & Robustness ยท What hellish is + Install