BATsh
view release on metacpan or search on metacpan
"1..N" line, and an end-of-run reconciliation reports
"planned X but ran Y" (setting a non-zero exit) when the emitted
plan does not match the number of ok()/not-ok() lines. Both
problems now FAIL immediately on a plain "perl t/foo.t", not only
under a real harness.
- t/lib/INA_CPAN_Check.pm: add selfcheck_suite(), which runs t/*.t
(and xt/*.t) in a child Perl and verifies one plan line per file,
plan == number of ok/not-ok lines, and no failures.
- pmake.bat: at "pmake dist" time, after the existing source checks,
run INA_CPAN_Check::selfcheck_suite() as check3 and abort the
build if any test file fails the plan-sanity check (disable with
--no-check3). Bump $PMAKE_BAT_VERSION to 0.34.
- t/lib/INA_CPAN_Check.pm: pass \@files / \@pm_files (a reference)
instead of [ @files ] (an anonymous copy) to _find_pm_t() in
_scan_code(), check_D(), check_E(), and check_K(). The copy form
meant the collected file list never reached the caller, so E1
(no shebang in lib/*.pm) and K3 (return { %hash } form) silently
scanned zero files and always passed.
- Documentation: BATsh.pm BUGS AND LIMITATIONS corrected. It no longer
claims SH-mode background execution is unsupported (it is supported
for external commands; see above and BATsh::SH), and it now clarifies
that non-builtin commands (FINDSTR, SORT, etc.) are invoked as
external programs rather than "unsupported". README and BATsh.pm POD
additionally enumerate previously undocumented limitations: CMD
"%VAR:~n,m%" / "%VAR:str1=str2%" and dynamic "%RANDOM%/%DATE%/%TIME%/
%CD%" variables; SH arrays, filename globbing, "~" tilde expansion,
brace expansion, and the trap/getopts/select/alias/declare/eval/exec
builtins and set -e/-u/-x options; and the shared (no sub-shell)
"( ... )" grouping common to both modes.
- SH expansion: a backslash-escaped "\$", "\`" or "\\" inside double
quotes is now preserved literally and no longer triggers variable
or command substitution (e.g. "\$_" yields a literal "$_").
- SH read: the "read" built-in now returns a non-zero status at end of
input so that "while read VAR; do ...; done < FILE" terminates
instead of looping. Leading option flags such as "-r" are skipped
and are no longer treated as target variable names.
- SH assignment prefix: "VAR=value command args" (POSIX) now applies
the assignment and then runs the command (e.g. "IFS= read -r LINE",
"LC_ALL=C sort"); multiple prefixes are supported. A standalone
assignment whose value merely contains spaces or a "$(...)"
substitution (e.g. UPPER=$(echo "a b")) keeps the full value and is
no longer mistaken for a prefix.
- SH while/until: an input redirection on the "done" line
("while read L; do ...; done < FILE") now reopens STDIN from FILE for
the duration of the loop so the loop's "read" consumes the file.
- eg/06_sh_comprehensive.batsh: I/O-redirection section simplified to a
plain "while read" loop now that the loop terminates correctly.
- Tests: t/9070-examples.t now executes each eg/*.batsh in a child
process and guards against runaway output and "syntax error"
breakage (E4). t/9060-readme.t verifies the README advertises every
eg/ example by name (R5). README gains an EXAMPLES section.
- SH background execution: an unquoted trailing "&" starts an external
command asynchronously and returns immediately. On Win32 the job is
spawned via system(1, ...) (P_NOWAIT, PID returned); on Unix it is
started through /bin/sh without a Perl fork, capturing the job PID
via the shell's $! into a sysopen O_CREAT|O_EXCL temp file (Pure
Perl, 5.005_03). The new $! parameter expands to the most recent
background PID (empty before any job); $? is 0 on a successful
launch (the job's own exit status is not awaited). Built-ins,
functions, assignments and control words ignore the trailing "&"
and run in the foreground; "&&", ">&"/"2>&1", quoted and escaped
"\&" are not treated as background. No job control; CMD-mode "&"
remains a sequential separator (see BUGS AND LIMITATIONS).
- eg/05_cmd_comprehensive.batsh: the "IF ERRORLEVEL" diagnostic line
"ECHO ERRORLEVEL>=0: ELTEST=%ELTEST%" contained a bare ">", which
CMD mode correctly treats as output redirection (matching cmd.exe).
As written, the message was silently redirected to a file named
"=0:" instead of being printed, and that stray file was created in
the current directory each time the example ran (including under
"make test" via t/9070-examples.t). The ">" is now caret-escaped
("ECHO ERRORLEVEL ^>= 0: ELTEST=%ELTEST%"), so the line prints
as intended and no file is written.
0.02 2026-04-28 JST (Japan Standard Time)
[Highlights]
- Full bash/sh interpreter implementation: if/for/while/until/case,
function definitions (name() { ... }), local variable scoping,
&& / || / ; compound commands, pipelines (|), I/O redirection
(> >> < 2> 2>> 2>&1), variable expansion (${var%pat}, ${var#pat},
${#var}, ${var^^}, ${var,,}, ${var:N:L}, ${var/p/r}, ${var//p/r}),
positional parameters $1..$9 / $@ / $* / $#, shift, read, source.
- cmd.exe pipeline (|) support via temporary file (Pure Perl, 5.005_03).
- I/O redirection: stdout overwrite (>), append (>>), stdin (<),
stderr (2>), stderr-to-stdout (2>&1), stdout-to-stderr (1>&2).
Supported in both CMD mode and SH mode.
- SH here-documents on STDIN: cmd <<DELIM ... DELIM, <<-DELIM (strip
leading tabs), and <<'DELIM' (literal, no expansion). Body is
materialised to a temp file created with sysopen O_CREAT|O_EXCL
(Pure Perl, 5.005_03) and fed through the existing "< file" path,
so both built-ins (read) and external commands see it on STDIN.
Top-level mode dispatch is here-document aware, so uppercase body
lines are not misrouted to CMD mode. Single here-document per line;
here-strings (<<<) and same-line pipeline/compound combos are not
supported (see BUGS AND LIMITATIONS).
- cmd.exe batch-parameter tilde modifiers: %~0, %~f1, %~dp0, %~nx1,
%~n0, %~x0, %~p1 etc. (f d p n x modifiers, combinable).
- SET /P VAR=Prompt interactive prompt input from STDIN.
- $0 normalised to absolute path via File::Spec on run().
[BATsh::Env]
- Variable names are now stored and looked up in uppercase, matching
cmd.exe's case-insensitive environment variable behaviour.
SET myvar=x followed by ECHO %MYVAR% now correctly outputs "x".
- Added $DELAYED_EXPANSION package variable (default 0).
- setlocal() now accepts an options string and parses
ENABLEDELAYEDEXPANSION / DISABLEDELAYEDEXPANSION.
The delayed-expansion flag is saved/restored with the variable store.
- expand_cmd() now expands !VAR! references when $DELAYED_EXPANSION is on.
[BATsh::CMD]
- Implemented ^ escape character:
( run in 0.510 second using v1.01-cache-2.11-cpan-9581c071862 )