NAME BATsh - Bilingual Shell for cmd.exe and bash in one script (self-contained) VERSION Version 0.03 SYNOPSIS use BATsh; # Run a bilingual .batsh script BATsh->run('myscript.batsh'); BATsh->run('myscript.batsh', args => ['arg1', 'arg2']); # Run source inline BATsh->run_string('echo hello from sh'); BATsh->run_string("SET MSG=hello\nECHO %MSG%"); # Interactive REPL BATsh->repl(); # CMD features: pipe, tilde modifiers, SET /P BATsh->run_string('ECHO hello | perl -e "while(){chomp;print uc(\$_).chr(10)}"'); BATsh->run_string("SET /P NAME=Enter name: "); # SH features: functions, expansions, pipelines, redirection BATsh->run_string(<<'BATSH'); greet() { echo "Hello, \$1" } greet world x=\$(echo hello | perl -e 'while(){chomp;print uc}') echo \$x echo out > /tmp/out.txt BATSH # Perl 5.005_03 and later; pure-Perl, no external shell required. DESCRIPTION Executive Summary BATsh is a self-contained bilingual shell interpreter written in pure Perl. It runs cmd.exe batch syntax and bash/sh syntax in the same script file, switching automatically between CMD mode and SH mode on a line-by-line basis. No external cmd.exe, bash, or sh is required -- everything runs inside Perl. Mixed-Mode Sample The following script demonstrates cmd.exe and bash sections coexisting and sharing variables through the common BATsh::Env variable store. :: -- CMD section: sets a variable and calls a SH function via bridge -- @ECHO OFF SET LANG=BATsh SET COUNT=3 # -- SH section: reads CMD variables, uses functions and pipeline -- greet() { echo "Hello from $1 (bash/sh mode)" } greet $LANG for i in 1 2 3; do echo " item $i of $COUNT"; done result=$(echo "$LANG" | perl -e 'while(){chomp;print uc}') echo "Uppercase: $result" echo "log line" >> /tmp/batsh_demo.txt :: -- CMD section again: reads variable set by SH side -- ECHO Back in CMD mode ECHO Uppercase result: %result% BATsh features (both modes): pipelines (|), I/O redirection (> >> < 2>&1), variable expansion (${var%pat} ${var^^} ${#var}), functions, shift, local. FULL DESCRIPTION BATsh is a self-contained bilingual shell interpreter written in pure Perl. It implements both the cmd.exe command set and the sh/bash command set entirely in Perl -- no external cmd.exe, bash, or sh is required. Scripts are divided into CMD sections (uppercase first token) and SH sections (lowercase first token). Both sections share a common variable store via BATsh::Env, so variables set in a CMD section are immediately visible in the next SH section and vice versa. CMD MODE Any line whose first token is all uppercase (A-Z, 0-9, path chars) is a CMD line. CMD sections are executed by BATsh::CMD, which implements: ECHO, @ECHO OFF/ON SET VAR=value, SET /A expr (arithmetic) SET /P VAR=Prompt (interactive prompt input from STDIN) IF "A"=="B" ... ELSE ..., IF /I (case-insensitive), IF NOT IF EXIST "path with spaces", IF DEFINED var, IF ERRORLEVEL n FOR %%V IN (list) DO ..., FOR /L %%V IN (s,step,e) DO ... FOR /F "tokens= delims= skip= eol= usebackq" %%V IN (src) DO ... GOTO :label, :label, GOTO :EOF CALL :label [args], CALL file.batsh SHIFT, SHIFT /N SETLOCAL [ENABLEDELAYEDEXPANSION|DISABLEDELAYEDEXPANSION], ENDLOCAL CD, DIR, COPY, DEL, MOVE, MKDIR, RMDIR, REN, TYPE PAUSE, EXIT [/B] [code], CLS, TITLE, VER, PUSHD, POPD cmd1 | cmd2 (pipeline via temporary file) &, &&, || (sequential, conditional-and, conditional-or) Variable Expansion "%VAR%" references are expanded before each line is dispatched. Variable names are case-insensitive ("SET foo=x" is visible as "%FOO%"). Inside parenthesised IF and FOR blocks, "%VAR%" is expanded at parse time (before any commands in the block run), matching cmd.exe behaviour. To see a value updated inside a block, use delayed expansion: SETLOCAL ENABLEDELAYEDEXPANSION SET X=old IF 1==1 ( SET X=new ECHO !X! &:: prints "new" (delayed) ECHO %X% &:: prints "old" (parse-time) ) ENDLOCAL Batch Parameters %0 is the script path (absolute); %1..%9 are positional arguments; "%*" is all arguments joined by space. "SHIFT" / "SHIFT /N" shifts the positional parameters. "CALL :label" saves and restores caller's arguments. Batch-parameter tilde modifiers expand %0..%9 components: %~0 dequote (strip surrounding "...") %~f1 full absolute path of %1 %~d1 drive letter only (e.g. C:) %~p1 directory path only (with trailing /) %~n1 filename without extension %~x1 extension only (e.g. .bat) %~dp0 drive + directory (most common usage) %~nx1 filename + extension Redirection and Compound Commands ECHO text > file stdout overwrite ECHO text >> file stdout append prog 2> err.txt stderr redirect & cmd sequential execution cmd1 && cmd2 run cmd2 only if cmd1 succeeded (ERRORLEVEL 0) cmd1 || cmd2 run cmd2 only if cmd1 failed (ERRORLEVEL != 0) The "^" character escapes the next character: ECHO a^&b prints a&b (& not treated as compound separator) ECHO a^^b prints a^b ECHO text^ next line is joined (line continuation) SH MODE Any line whose first token contains a lowercase letter is a SH line. SH sections are executed by BATsh::SH, which implements: VAR=value, export VAR=value, unset VAR echo, printf if/then/elif/else/fi for VAR in list; do ... done while condition; do ... done until condition; do ... done case $var in pattern) ... ;; esac test / [ ... ] (file, string, and integer comparisons) cd, pwd, exit, true, false, :, read, shift [N], local VAR=value $(( arithmetic )) -- +, -, *, /, %, and $1..$9 inside $( command ) and `command` (command substitution, nested) cmd1 | cmd2 [| cmd3 ...] (pipeline via temporary file) cmd1 && cmd2, cmd1 || cmd2, cmd1 ; cmd2 (compound commands) > >> < 2> 2>> 2>&1 1>&2 (I/O redirection) cmd << DELIM ... DELIM (here-document; <<-DELIM, <<'DELIM' too) cmd & (background execution; external commands) name() { ... }, function name { ... } (function definitions) $VAR, ${VAR}, $1..$9, $@, $*, $#, $?, $$, $0, $! ${VAR:-default}, ${VAR:=default}, ${VAR:+alt} ${VAR%pat}, ${VAR%%pat} -- shortest/longest suffix removal ${VAR#pat}, ${VAR##pat} -- shortest/longest prefix removal ${VAR/pat/rep}, ${VAR//pat/rep} -- first/all substitution ${VAR^^}, ${VAR^}, ${VAR,,}, ${VAR,} -- case conversion ${VAR:N:L}, ${VAR:N} -- substring ${#VAR} -- string length source / . file REQUIREMENTS Perl 5.005_03 or later. Core modules only. No external shell required. BUGS AND LIMITATIONS Commands that are not built in -- "FINDSTR", "SORT", "MORE", "CHOICE", "TIMEOUT", "XCOPY", "ROBOCOPY" and the like in CMD mode, and any non-builtin program in SH mode -- are NOT reimplemented in Perl. They are invoked as external programs (via Perl's "system"), so they work only where the host operating system provides the corresponding executable (e.g. FINDSTR.EXE on Windows). Only the built-in command set is guaranteed to run identically on every platform. The built-in CMD interpreter does not implement: * Variable substring "%VAR:~n,m%" and substitution "%VAR:str1=str2%" expansion. * Dynamic pseudo-variables "%RANDOM%", "%DATE%", "%TIME%", "%CD%", "%CMDCMDLINE%", and "%ERRORLEVEL%" used as a variable reference. * "FOR /F" with "usebackq" backtick-quoted commands on Windows (the "cmd /c" subprocess path is untested on Windows). The built-in SH interpreter does not support: * Arrays, indexed and associative ("arr[i]", "${arr[@]}", "declare -A"). * Filename (pathname) globbing such as "echo *.txt" or "for f in *.pl". The "*", "?" and "[abc]" metacharacters are honoured in "case" patterns and in "${VAR%pat}" / "${VAR#pat}" expansion only, not for expanding filenames on the command line. * Tilde expansion "~/path" and "~user" (only "cd" with no argument uses "$HOME"). * Brace expansion "{a,b}" and "{1..5}". * Here-strings ("<<< word") and process substitution ("<(cmd)", ">(cmd)"). * The shell options "set -e", "set -u", "set -x", and the builtins "trap", "getopts", "select", "alias", "declare"/"typeset", "eval" and "exec". Common to both modes: a parenthesised group "( ... )" does not run in a separate sub-shell; it shares the one variable store, so there is no variable-scope isolation. Here-documents ("<<", "<<-", "<<'DELIM'") ARE supported on STDIN, with these limitations: * Recognised only in SH mode; "<<" has no meaning in CMD mode. * One here-document per command line only (no "cmd <&"/"2>&1" fd duplication, in quotes, or escaped as "\&" is not background. * No job control: "jobs", "wait", "wait %n", "fg", "bg" and "%n" job specifications are not implemented; signals are not delivered to background jobs. * A backgrounded pipeline or compound list is delegated as a unit to the OS shell after BATsh expands variables and substitutions, so its redirections and operators follow OS-shell rules. * On a successful launch "$?" is 0; the job's own exit status is not awaited. "$!" holds the most recent background PID (empty before any job). On Win32 the job is spawned via "system(1, ...)"; on Unix it is started through /bin/sh without using a Perl fork. Pipeline ("|"), I/O redirection (">" ">>" "<" "2>" "2>>" "2>&1"), compound commands ("&&" "||" ";"), and function definitions are all supported. Section boundary detection is token-based (uppercase vs. lowercase first token). Mixed-case first tokens are treated as SH. Please report bugs via the issue tracker: EXAMPLES The eg/ directory contains runnable example scripts: eg/00_hello.pl Minimal Perl driver calling BATsh->run eg/01_hello.batsh Hello world in both modes eg/02_env_bridge.batsh Environment-variable bridge (CMD <-> SH) eg/03_cmd_features.batsh CMD-mode features and parameter modifiers eg/04_sh_features.batsh SH-mode features and expansions eg/05_cmd_comprehensive.batsh Comprehensive CMD-mode tour eg/06_sh_comprehensive.batsh Comprehensive SH-mode tour eg/07_mixed_comprehensive.batsh Mixed CMD/SH comprehensive tour Run a .batsh example with: perl -Ilib -MBATsh -e "BATsh->run(shift)" eg/01_hello.batsh SEE ALSO BATsh::CMD, BATsh::SH, BATsh::Env AUTHOR INABA Hitoshi LICENSE This software is free software; you can redistribute it and/or modify it under the same terms as Perl itself.