BATsh
view release on metacpan or search on metacpan
- Script exit-code propagation and $? <-> %ERRORLEVEL% bridge.
run() / run_string() / run_lines() now RETURN the script's final
exit status: "exit 3" gives 3, "EXIT /B 5" gives 5, otherwise the
status of the last command. The modulino exits with that code
(exit(BATsh->main(@ARGV))), so OS-level %ERRORLEVEL% / $? of
"perl lib/BATsh.pm script.batsh" is the script's own status.
At every CMD<->SH section boundary the status is mirrored both
ways, so a failing SH command is visible as %ERRORLEVEL% in the
next CMD section and vice versa. New public accessor
BATsh->last_status. New sub main() handles --help, --version,
-e 'source', and "-" (read script from STDIN, remaining arguments
become %1../$1..). EXIT with no code keeps the current
ERRORLEVEL. "exit N" now also ends the REPL. NEW FILE
bin/batsh.pl (EXE_FILES); installed as "batsh".
- $(( )) arithmetic rewritten as a recursive-descent parser.
Now supported: comparison and logical operators (result 0/1),
assignment operators = += -= *= /= %= <<= >>= &= ^= |= (written
back to the variable store), prefix/postfix ++ and --, the
ternary ?: and comma operators, ** (right-associative), bitwise
& ^ | ~ << >> (~ is signed: -v-1), hex 0x.. and octal 0..
literals, and C-style truncation toward zero for / and %
(-7/2 = -3, -7%2 = -1). Errors warn and yield 0. Fixed a
here-document false positive on $((1<<4)) ("<<" inside $(( ))
is no longer taken as a heredoc opener).
- SH shell options set -e / -u / -x (and set -o errexit /
nounset / xtrace; +e/+u/+x to turn off; options combinable as
in "set -eux"). set -e stops the script with the failing
command's status; if/while/until conditions and non-final
members of && / || lists are exempt. set -u makes expansion of
an unset variable an error (warning + exit status 1;
${VAR:-default} is exempt). set -x traces each simple command
to STDERR with a "+ " prefix. Options are reset at the start
of each top-level run so "set -e" cannot leak into a later
run() in the same process. Limitations are documented in POD:
-x traces the raw pre-expansion line (expanding a copy would
run $(...) twice), and -u stops after the current command
completes with the empty expansion.
- NEW SH BUILTIN eval: one level of quote removal, concatenation,
and re-execution with a second round of expansion (POSIX
semantics). Previously "eval" fell through to external-command
execution and only worked by accident where /bin/sh handled it.
- New tests: t/0016-exit-status.t, t/0017-sh-arith.t,
t/0018-sh-set-options.t.
- Fixed t/0016-exit-status.t (ES12-ES14) on real Windows: the
child-process helpers used a single quoted shell string for
system(), which cmd.exe re-wraps in an extra pair of double
quotes and mis-parses ("filename, directory name, or volume
label syntax is incorrect"). Rewritten to use LIST-form
system() (bypasses the shell entirely) with STDIN/STDOUT
redirected at the Perl level via dup/reopen instead of a
shell "<"/">" string. lib/BATsh.pm itself was unaffected; this
was a test-harness-only bug. Re-verified on Linux (16/16).
Desk review of the Win32-specific code paths (no Windows host
available in this environment): BATsh::SH::_bg_launch_decoded()
uses system(1, $cmdline) (P_NOWAIT) to background a job on
Win32; the success/failure check (defined $pid && $pid > 0)
matches the documented Win32 system(1, ...) contract (PID on
success, 0 on failure). t/0016's _run_child() LIST-form
system($^X, "-I$lib", $pm, $prog, @args) calls CreateProcess
directly and does not re-enter cmd.exe, so the original
quote-re-wrapping failure mode cannot recur. _bg_tempfile()'s
temp-directory selection already accounts for %TEMP%/%TMP% and
backslash path separators. No logic defects found in review;
confirmation on an actual Windows host (locale, path-length,
%TEMP% permission variables that cannot be reviewed statically)
remains pending.
- CP932 (Shift_JIS) multibyte-safe execution: NEW MODULE BATsh::MB.
A .batsh script written in CP932 -- the encoding of Japanese
Windows -- now runs correctly even when its characters contain
trail bytes that collide with ASCII shell metacharacters (the
classic "dame-moji" / 0x5C problem). Affected characters are
extremely common: SO (0x835C), HYOU (0x955C), NOH (0x945C) end
in a backslash; PO (0x837C) in a pipe; CHI (0x8360) in a
backtick; DA (0x835E) in the cmd.exe caret escape; and many
trail bytes fall in a-z/A-Z where uc()/lc() corrupt them.
Design: instead of teaching every byte-oriented scanner in
BATsh::CMD / BATsh::SH about lead and trail bytes, the script
text passes through a reversible GUARD TRANSFORM on input. Each
two-byte character LEAD+TRAIL whose TRAIL is in the dangerous
ASCII range 0x40-0x7E is rewritten to the three-byte form
\x01 LEAD (TRAIL+0x80), which contains no ASCII bytes; a literal
\x01 becomes \x01\x01, making the transform bijective. All
existing parsing (caret escapes, pipelines, quotes, redirects,
globs, case patterns, uc/lc variable handling, SETLOCAL
snapshots, ...) is then automatically DBCS-safe with no scanner
changes. The inverse transform is applied at the output
boundaries only:
print sinks ECHO/echo/printf/SET display, prompts, _warn
exec sinks system() for external commands, background jobs,
FOR /F ('command') input pipes
file sinks redirect targets, CD/DIR/COPY/DEL/MOVE/MKDIR/
RMDIR/REN/TYPE paths, IF EXIST, test -e/-f/-d...,
glob patterns, here-document bodies, CALL/source
script filenames
env sink BATsh::Env::sync_to_env (raw bytes into %ENV)
and input re-entry points guard incoming raw bytes again:
SET /P and read line input, FOR /F file/command lines, $(...)
and `...` captured output, glob results, Cwd for %CD%/PWD.
- Encoding selection (BATsh::MB): 'auto' is the default -- a
non-UTF-8 source containing bytes >= 0x80 is detected as CP932
and the guard switches on (and then stays on for the process,
because Env may hold guarded values; only an explicit
set_encoding deactivates it). Pure-ASCII and well-formed UTF-8
sources need no guarding and are byte-for-byte unaffected, so
existing behaviour is preserved exactly. Explicit selection:
BATsh->run($file, encoding => 'cp932');
BATsh->run_string($src, encoding => 'sjis');
BATsh->set_encoding('cp932');
( run in 0.757 second using v1.01-cache-2.11-cpan-9169edd2b0e )