Changes for version 0.04 - 2026-11-03

  • The documents of only historical interest are no longer shipped; they are in Proc-Spawner 0.03 on CPAN: doc/large-subset/large-subset- {ceiling,investigation,marks,upstream-reports}.md, doc/pspawn-run- shortcomings.md, doc/pspawn-upstream-reports.md, doc/parsers/shell- subset-corpus-stats.md. References to them say so.
  • The figures of post4-0.03 were checked again on 0.04: the same per-sample results; they are labelled 0.04. Digest of the code they depend on: ecde8b9b9610680560a69e56313f01687f29e51688bcd029c6cef837fa03eeac. The documents label the unreleased states between 0.03 and 0.04 post-0.03 to post4-0.03; their entries follow, newest first.
  • post4-0.03:
    • Rule E (the user, 2026-11-03), replacing A3: where dash and bash differ, the subset takes the plainly logical behaviour, and rejects the form only where there is none. doc/large-subset/large-subset-logical.md lists every difference with its category (info, error, posix, same, harm, platform, meaning, effect, none) and checks the earlier decisions against the rule. Done so far: jobs, jobs -p, jobs -l list the jobs also in a pipeline and in $( ) (as bash; this replaces the rejection of post3-0.03); return outside a function is an error (as bash); cd with extra operands or without HOME is an error (as bash); descriptors above 9 (option fdmulti now on by default); [^...] negates; patterns never match . or ..; settings with a redirection are redirected before their values are expanded (as dash). getopts leaves OPTARG unset when it finds no option, or an option without an argument (POSIX; as bash; dash keeps or empties it). Battery t/batteries/large-subset-e.txt (the differences from dash are listed in t/dash-known.txt as rule E).
    • Forms both shells run alike, now accepted: reading $PPID (assigning it stays rejected), export "$name", symbolic umask modes, redirections on cd, and the top-level jobs -p.
    • X19 fixed: a trap's action now runs before the next command of the list (it could run after it, about 1 run in 30). New known issue X24.
    • The detector: the choices of rule E are class X with basis E:CATEGORY; ShellCheck's SC3023 and SC3026 likewise; local and return outside a function are separate features; local=... is no longer taken for local.
    • Figures: 95 samples rejected within the lower yardstick (post3-0.03: 106); digest 84245e1d9a2ad88da10b320f8d2e94ddeb0b12bd888925555b920d985026e10a.
  • post3-0.03:
    • Proc::ShellSubset::Large rejects jobs in a pipeline, a command substitution or a compound command: there dash lists nothing and bash the jobs (probes jobspipe, jobsppipe, jobspsubst). Tests LS:288-292.
    • The detector has two more blatant features: jobs in a pipeline or $( ) (jobsctx), and cd with extra operands (cdargs: dash ignores them, bash fails). Its word pattern now keeps $( ), backquotes, ${ } and backslash-escapes in one word. 11 new probes (jobs, cd, the P6 forms) in doc/large-subset/dialect-cases/large-subset-dialect-probes2.txt.
    • Figures: 106 samples rejected within the lower yardstick (post-0.03: 117); digest 64810b85d9f4cc777ef7d41f3cc3f9f4262c1c56cf877ba00489dbce07b117e8.
  • post2-0.03:
    • pwd runs as the program /bin/pwd -L, with PWD set to the shell's own directory, and its operands dropped (known issue X23; the user's choice C, 2026-11-03): after cd through a symbolic link it prints the directory as reached, as both shells (before: the physical path). Two more lines in t/batteries/large-subset-p6.txt (21 of 21 agree with dash). The parsers did not change, so the figures of post-0.03 hold.
  • post-0.03:
    • Proc::ShellSubset::Large accepts more forms both shells run alike (item P6 of "Shell Corpus Gaps"; probes in doc/large-subset/dialect-cases): read -p PROMPT (also -rp; the executor writes the prompt to fd 2 only when fd 0 is a terminal, as both shells); cd -P and cd -L (the last one counts; the tree gets cd_p => 1, and the executor resolves symbolic links); a pattern in a redirection target is literal (echo a >*.log writes the file '*.log', as dash and bash in POSIX mode); set -o ignoreeof, notify, nolog, set -b and set +m (nothing to do); trap with signal names in any case and signal numbers, also FPE, and KILL and STOP (cannot be caught: nothing to do, status 0). Tests LS:273-287 (LS:114 now accepted), and t/batteries/large-subset-p6.txt (19 of 19 agree with dash).
    • pwd with an option both shells refuse (pwd -x, pwd --help) is rejected (rule Q1; test LS:111 changed). Known issue X23: a bare pwd after cd through a symbolic link differs from both shells (the program /bin/pwd, decision B2 of the base parser's checklist) - a question to the user.
    • The detector (tools/large-subset-features.pl): a read -p prompt with quotes no longer hides the options after it (read -p "`echo -e x`" m was taken for read -e), read -p PROMPT without a name is the bash-only REPLY form, and set -o pipefail and the like are seen after other options (set -euo pipefail).
    • Figures (doc/large-subset/large-subset-state.md): the samples rejected within the lower yardstick went from 163 (0.03) to 117. They depend on the parsers, the measurement scripts and the verdicts, whose sha256 (the command: doc/large-subset/large-subset-state.md, section 3) is 4921dfbd1b838bc48dabe234a5111ad69d8fbb7d43c8278c72dcf74a5ad9b7bf.

Documentation

a map of the design documents shipped in doc/ (EXPERIMENTAL)

Modules

builtins of the large shell subset: alias, the job builtins, and the interactive ones (EXPERIMENTAL)
parse a Bourne-shell subset into a tree (EXPERIMENTAL)
parse as much of POSIX sh as dash and bash run alike (EXPERIMENTAL)
the Proc::ShellSubset grammar with its options frozen (EXPERIMENTAL)
start children with posix_spawn(3) and run the one event loop (EXPERIMENTAL)

Provides

in lib/Proc/ShellSpawn.pm
in lib/Proc/ShellSpawn/Large.pm
in lib/Proc/Spawner/Graph.pm