Revision history for Proc-Spawner (EXPERIMENTAL)
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.
0.03 2026-11-03
- Proc::ShellSubset::Large accepts forms dash and bash run alike (items
P1-P4 of "Shell Corpus Gaps"): '{' and '}' inside words where no brace
expansion can start; $( ), `...`, ${#NAME} and $((...)) inside $((...));
setting PS1, PS4, HOSTNAME, PIPESTATUS and BASH; export $v[=value];
exit N above 255; umask -S; command -p PROGRAM. '{' or '}' as a
command name, and '{NAME}' right before a redirection (bash: a named
descriptor), stay rejected (tests LS:212-245).
- Proc::ShellSpawn, Proc::ShellSpawn::Large: run those forms ($( ) inside
$(( )) expanded first; exit N modulo 256; umask -S; command -p with
/usr/bin:/bin; export with a name from an expansion, checked at run time).
- The borrowed test pool: the scripts that made
t/parser/shell-subset-corpus-tests.pl are now in doc/parsers/tools/
(extract, run, group, gentests, prov, verify and the reports), with
the grouping of 2026-10-29 (groups.tsv); doc/test-pools.md says how to
rebuild every pool and measurement from the pinned sources (checked:
all 612 snippet IDs restore their texts exactly).
- Measuring against dash and bash by the shells alone:
doc/large-subset/large-subset-state.md (the rules, which beats which,
the figures, the work list); large-subset-dialect-features.md (every
probed feature, blatant or subtle, with who decided it);
tools/large-subset-features.pl (the detector, and the per-test record
t/parser/large-subset-shells.tsv), tools/large-subset-verdicts.txt
(verdicts kept by hand, postponed ones as free text), and a new
tools/large-subset-yardsticks.pl (the old one sorted our parser's
own messages).
- doc/pspawn-known-issues.md: X21 (the programs echo, [, printf, kill on
Windows), X22 (bash 5.2.21 goes on after some special-builtin errors,
against its manual).
- Items the user agreed to on "Shell Corpus Gaps" (2026-11-03):
B1 alias and unalias (the parser expands aliases in the lines after a
top-level definition, as both shells do; the executor keeps the table
for eval and . text); B2 history, fc, bind, help, compopt, logout and
set -o history do nothing by default (parser and run() option
interactive => reject|nop|dash), and the executor looks command names
up in a list of tables, @Proc::ShellSpawn::CMDS, which other modules can
extend; B3 jobs, bg, fg, hash, type, times and reading ulimit, with
dash's texts and statuses, in the new module Proc::ShellSpawn::Builtins;
P5 and R1 [ and test are the program, and '<' or '>' between two
operands need the parser option test_lt_gt; Y2 a backslash at the very
end is a literal \; Y3 tldr's <Enter> in one-line here-documents, and
lines with keystrokes left out of the measurement; Q1 kill with a SIG
signal name is rejected (both shells refuse it).
- Every measured figure in doc/ names the version it was measured on
(pre-0.01, 0.01, 0.02, early-post-0.02, 0.03; the table is in
doc/README.md). The figures of 0.03 were measured again on the release:
the corpus yardsticks, the work list and the totals over the tests came
out the same. Corrected: spec section 8's ShellCheck figures (now as in
the ceiling document), test-pools.md's size of the per-test record
(1,066 lines), and the count of samples accepted only thanks to class X
(258, not 259); test-pools.md's regrouping count is of 0.03 (577 groups,
checked).
- doc/checklists/corpus-gaps.html is the page "Shell Corpus Gaps" as
published (its third version, with the figures of 0.03), and
doc/large-subset/pages/gaps-items.txt.lede its lede (the lede had been
edited only in the published page). t/dash-known.txt no longer lists the
line of largebattery.txt with an alias as a known difference: since B1 it
agrees with dash, so a change there now fails t/dash.t.
- lib/Proc/Spawner/Docs.pod: a map of the Markdown documents in doc/;
doc/large-subset/large-subset-state.md.html: the same text as the .md,
with a Markdeep footer (an experiment in how MetaCPAN shows them).
- Proc::ShellSubset::Large no longer claims the package name
Proc::ShellSubset for the PAUSE indexer (MetaCPAN showed it as the
home of Proc::ShellSubset).
0.02 2026-11-02
- Documentation: POD for Proc::ShellSubset, Proc::ShellSubset::Small and
Proc::ShellSubset::Large; doc/large-subset/ (the large parser's
specification, how its corpus coverage is measured, with the scripts in
doc/large-subset/tools/, and its history); doc/parsers/ (the base
parser's grammar and the provenance of its corpus tests); three more
decision pages in doc/checklists/.
- t/parsers.t also checks that Proc::ShellSubset::Small parses every test
string as Proc::ShellSubset does, and, with dash and bash in PATH, that
both shells accept what only the large grammar accepts; the checks that
need dash and bash are skipped without them (they used to fail).
- The message for a construct outside the subset found at run time
(status 248) now names Proc::ShellSubset::Large, not ShellSubsetLarge.
- README: the aim is matching dash/bash (what both do alike), not dash
alone; also portable shell scripts; every started program is a child of
the Perl process. Proc::ShellSpawn's header says the same.
- Every module has $VERSION.
- t/dash.t: each run has its own tmp/ for the battery strings that wrote fixed
files in /tmp, and ignores the length of process IDs turned into N.
Test runs at the same time no longer fail each other (probably the
unexplained failure X20). The kill -INT $$ strings are marked FLAKY:
X19 also shows with a subshell.
0.01 unreleased (2026-11-02)
- First version: Proc::Spawner (posix_spawn with every glibc 2.39 setting, the
one event loop), Proc::Spawner::Graph (the executable spawn graph),
Proc::ShellSpawn (runs shell one-liners without fork), and the parsers
Proc::ShellSubset, Proc::ShellSubset::Small, Proc::ShellSubset::Large.
- Builds with older glibc (seen failing on 2.36): Makefile.PL probes for each
posix_spawn extension, and an option the libc lacks follows UNSUPPORTED.
- No "Hexadecimal number > 0xffffffff" warning from t/spawner.t or $((0x...)).
- Tests: t/graph.t, t/parsers.t, t/dash.t besides t/spawner.t.
- Former names: PosixSpawn (now inside Proc::Spawner), ShellSpawn,
ShellSubset, ShellSubsetSmall, ShellSubsetLarge.
Keyboard Shortcuts
Global
s
Focus search bar
?
Bring up this help dialog
GitHub
gp
Go to pull requests
gi
Go to GitHub issues (only if GitHub is preferred repository)