Proc-Spawner version 0.06 (EXPERIMENTAL)
========================================

Compiles and runs shell one-liners (or portable shell scripts) from Perl
without /bin/sh and "without" fork, matching dash/bash: the strings are
parsed by one of the Proc::ShellSubset parsers and run
by Proc::ShellSpawn, which starts programs with posix_spawn(3) and runs loops,
subshells, $( ) and pipelines of compound commands inside the Perl process.
(All the started programs are children of the initial Perl process, as opposed
to descendants.)

  Proc::Spawner          start children (every posix_spawn setting); the event loop
  Proc::Spawner::Graph   the executable graph of children, pipes and files
  Proc::ShellSpawn       the shell layer
  Proc::ShellSubset, Proc::ShellSubset::Small, Proc::ShellSubset::Large
                         parsers of three sizes of the shell language

HOW CLOSE TO DASH AND BASH (0.06; doc/large-subset/large-subset-state.md)

Of 41,952 shell strings from six public collections (tldr, NL2Bash,
ShellCheck's tests, the bash Cookbook, the pure sh and bash bibles), the
large parser accepts 40,410.  The rest are mostly bash-only syntax, which the
subset rejects on purpose; 44 are forms both shells run alike, the work list
(section 4).  Each accepted string was then run by dash, by bash --posix and
by Proc::ShellSpawn, isolated, with every program but the plain text tools
replaced by a recorder of its arguments (section 7; the limits: known issue
X27):

                       run   same   only the    dash and bash  dash and bash
                                    messages    agree, ours    differ
                                    differ      differs
  coreutils involved  1,344    892     313            5            131
  no coreutils       39,066 14,859  23,083            2          1,108
  all                40,410 15,751  23,396            7          1,239
  (and 17 strings that never end, or not in ours: section 7)

"Coreutils involved": the string calls echo, printf, test, [, kill or pwd,
which the subset runs as programs where the shells have builtins.  "Only the
messages differ" is mostly dash's "x: not found" against bash's "x: command
not found".  Where dash and bash differ, the subset follows dash in 971,
bash in 98, and neither in 170 (mostly $0, dates, random data).  The 7
where ours differs: printf -v (2, decided), printf '%0.s' (1, open), echo
into a broken pipe in a subshell (1), and 3 that depend on chance.

For the programs alone, each call the strings make was also run as dash's
builtin, as bash's builtin and as the program: of 757 calls (1,268 uses),
620 (1,107) agree, 42 (46) differ in messages only, and 95 (115) differ:
mostly echo -e and backslashes (dash's builtin against bash and the
program), unary -a -o -v in [ ] (bash's builtin against dash and the
program), and kill with a word that is no process ID (dash against bash and
the program).  Section 7 has them all, with the decisions taken so far.

Tested on Linux with glibc 2.39 (also built as if some or all of the
extensions were missing: PROC_SPAWNER_WITHOUT="PIDFD_SPAWN ..." perl Makefile.PL),
and with /proc hidden, as on FreeBSD (RT #182479; FreeBSD itself is not tested
here): without /proc, t/graph.t and two batteries of t/dash.t are skipped.  Parts that flip process-wide
state for a while (umask, signal actions) are bullet-proof only in
non-threaded execution.

INSTALLATION

To install this module type the following:

   perl Makefile.PL
   make
   make test
   make install

DEPENDENCIES

Only modules that come with Perl.  Building needs a C compiler.
Makefile.PL finds which posix_spawn extensions the libc has (chdir, fchdir,
closefrom, tcsetpgrp, pidfd_spawn, cgroup) by compiling a call to each; an
option the libc lacks fails with ENOTSUP, or is dropped, as
%Proc::Spawner::UNSUPPORTED says.  The shell layer needs chdir (glibc 2.29
or later); without it t/dash.t is skipped.

Tests: t/spawner.t (Proc::Spawner and the graph), t/graph.t (hand-written
graphs), t/parsers.t (1,117 strings through the parsers), and t/dash.t
(every battery string run by dash and by the shell layer; skipped without
dash; t/dash-known.txt lists the known differences).  Their fixtures are
in t/: t/batteries/, t/parser/, t/sgraph/, t/try.pl (runs one string: perl t/try.pl 'STRING').

COPYRIGHT AND LICENCE

Copyright (C) 2026 by Ilya Zakharevich

This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.22.2 or,
at your option, any later version of Perl 5 you may have available.
