Revision history for BATsh
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.
- 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:
^X -> literal X (protects & | < > etc.)
^^ -> literal ^
trailing ^ -> line continuation (joins next line)
- Implemented I/O redirection parsed before command dispatch:
>file stdout overwrite
>>file stdout append
2>file stderr overwrite
2>>file stderr append
<file stdin redirect
Redirects with ^> are correctly treated as escaped > (not a redirect).
fd-digit stripping limited to isolated '1' or '2' before '>' to avoid
consuming trailing digits of command arguments (e.g. "ECHO line1 >f").
- SETLOCAL now passes its option string to BATsh::Env::setlocal() so
ENABLEDELAYEDEXPANSION and DISABLEDELAYEDEXPANSION take effect.
- !VAR! delayed expansion: pre_expanded block bodies now call expand_cmd()
at runtime when delayed expansion is active, so SET inside an IF/FOR
block followed by ECHO !VAR! correctly reflects the updated value.
- IF block pre-expansion: %VAR% in parenthesised IF/ELSE bodies is now
expanded at parse time (matching cmd.exe semantics), so a SET inside the
block does not affect %VAR% references in the same block.
- FOR block pre-expansion: %VAR% in parenthesised FOR bodies is expanded
once before the first iteration (at FOR-line parse time) and cached;
the loop variable is substituted per-iteration via an internal placeholder.
- IF /I (case-insensitive comparison) is now parsed before plain == so
that "/I" is not consumed as part of the left-hand operand.
- IF EXIST now handles quoted paths that contain spaces.
- ECHO no longer resets ERRORLEVEL to 0 after printing.
- FOR /F fully implemented:
tokens=N,M-P select specific token columns
tokens=N* select token N and put the remainder in the next variable
delims=CHARS field delimiters (default space/tab)
skip=N skip the first N lines of the source
eol=C skip lines beginning with character C (default ;)
usebackq swap quoting: "file" reads a file, 'cmd' runs a command
Sources: bare filename, quoted filename, 'command' (backtick output),
and ("literal string").
- & (sequential), && (conditional-success), || (conditional-failure)
compound commands are now supported.
- SET VAR=value: variable name regex relaxed to accept any non-'=' prefix,
matching cmd.exe's permissive variable naming.
[BATsh::SH]
- Full bash/sh interpreter implemented as Pure Perl (no external shell).
- Control structures: if/then/elif/else/fi, for/do/done, while/do/done,
until/do/done, case/esac with glob-pattern matching.
- Function definitions: name() { ... } and function name { ... } syntax,
including inline single-line bodies. Functions receive positional
arguments $1..$9; caller's arguments are saved and restored on return.
- local variable scoping: local VAR=value saves the caller's value and
restores it when the function returns.
- Compound commands: cmd1 && cmd2, cmd1 || cmd2, cmd1 ; cmd2.
- Pipeline: cmd1 | cmd2 [| cmd3 ...] implemented via temporary files
(Perl 5.005_03 compatible bareword filehandles, no fork/exec).
- I/O redirection: > >> < 2> 2>> 2>&1 1>&2, parsed after variable
expansion so that filenames may contain variables.
- Variable expansion: $VAR, ${VAR}, $1..$9, $@, $*, $#, $?, $$, $0.
Parameter expansion forms: ${VAR:-default}, ${VAR:=default},
${VAR:+alt}, ${VAR%pat}, ${VAR%%pat}, ${VAR#pat}, ${VAR##pat},
${VAR/pat/rep}, ${VAR//pat/rep}, ${VAR^^}, ${VAR^}, ${VAR,,}, ${VAR,},
${VAR:N:L}, ${VAR:N}, ${#VAR}.
Glob patterns in %/%%/#/## support *, ?, and [abc] character classes.
- Arithmetic expansion: $(( expr )) with +, -, *, /, %, and positional
parameters $1..$9 inside the expression.
- Command substitution: $( cmd ) with full nesting/quoting support, and
backtick `cmd` form.
- shift [N]: shifts positional parameters left by N positions (default 1),
updating both %N and %* in BATsh::Env.
- read VAR: reads one line from STDIN, chomps it, stores in VAR.
- source / . file: executes an external file in the current SH context.
- Builtin commands: echo, printf, cd, pwd, exit, true, false, :, export,
unset, set (noop), test / [ ... ] with -f -d -e -r -w -x -s -z -n
and string (= == != < >) and integer (-eq -ne -lt -le -gt -ge) ops.
- _cmd_subst uses fixed bareword filehandles (_SUBST_SAVOUT etc.) to
avoid collision under Perl strict and recursive invocations.
- _sh_strip_redirects and _sh_exec_with_redirs added for I/O redirection.
- _replace_cmd_subst added: walks $( ) depth-tracking the nesting so that
$( cmd | perl -e "print uc" ) parses the closing ) correctly.
- _split_sh_compound and _exec_sh_compound added for && / || / ; handling.
- _split_sh_pipe and _exec_sh_pipe added for pipeline handling.
- $0 is set to the absolute path of the running script by BATsh::run().
[BATsh.pm]
- _exec_cmd_section no longer intercepts SETLOCAL/ENDLOCAL itself;
both are passed through to BATsh::CMD::_dispatch so the option string
(ENABLEDELAYEDEXPANSION etc.) is correctly forwarded.
0.01 2026-04-26 JST (Japan Standard Time)
- Initial CPAN release.