Changes for version 0.02 - 2026-04-28
- 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.
Documentation
Modules
Bilingual Shell for cmd.exe and bash in one script (self-contained)
Pure Perl cmd.exe interpreter for BATsh
Shared variable store for BATsh
Pure Perl bash/sh interpreter for BATsh