NAME

BATsh::SH - Pure Perl bash/sh interpreter for BATsh

SYNOPSIS

# Used internally by BATsh; not normally called directly.
# BATsh::SH implements the SH-mode interpreter invoked when BATsh
# detects a bash/sh section in a .batsh script.

# Executed via BATsh:
use BATsh;
BATsh->run_string(<<'END');
x=hello
greet() {
    echo "Hello, $1 -- ${#1} chars"
}
greet world
echo ${x^^}
for i in 1 2 3; do
    echo item $i
done
ls /tmp | perl -e 'while(<STDIN>){print}'
echo out > /tmp/out.txt
END

DESCRIPTION

Executive Summary

BATsh::SH is the sh/bash interpreter component of BATsh. It handles any script section whose first token contains a lowercase letter, executing it entirely in Pure Perl -- no external shell required. It supports pipelines (|), I/O redirection (> >> 2>&1), functions, compound commands (&&/||/;), and rich parameter expansion: ${var%pat}, ${var^^}, ${var:N:L}, ${#var}.

Mixed-Mode Sample (via BATsh)

use BATsh;
BATsh->run_string(<<'SCRIPT');
:: CMD section: uppercase first token
SET CITY=Tokyo

# SH section: lowercase first token
greet() { echo "Hello from $1!"; }
greet $CITY
echo "lower: ${CITY,,}"
echo $CITY | perl -e 'while(<STDIN>){chomp;print uc,chr(10)}'
SCRIPT

FULL DESCRIPTION

BATsh::SH implements the POSIX sh / bash command set entirely in Perl. No external sh or bash is required.

Supported Features

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 tests, string, integer comparisons)
cd, pwd, exit, true, false, :, read, shift, local, set
$(( arithmetic )) -- supports $1..$9 positional params
$( command substitution ), `backtick substitution`
$VAR, ${VAR}, $1..$9, $@, $*, $#, $?, $$
${VAR:-default}, ${VAR:=default}, ${VAR:+alt}
${VAR%pat}, ${VAR%%pat}  -- suffix removal (shortest/longest)
${VAR#pat}, ${VAR##pat}  -- prefix removal (shortest/longest)
${VAR/pat/rep}, ${VAR//pat/rep}  -- substitution (first/all)
${VAR^^}, ${VAR^}, ${VAR,,}, ${VAR,}  -- case conversion
${VAR:offset:length}, ${VAR:offset}  -- substring
${#VAR}  -- string length
source / . file
name() { ... }, function name { ... }  -- function definition
cmd1 | cmd2 [| cmd3 ...]  (pipeline via temporary file)
cmd1 && cmd2  (run cmd2 only if cmd1 succeeds)
cmd1 || cmd2  (run cmd2 only if cmd1 fails)
cmd1 ; cmd2   (sequential execution)
> file, >> file, < file  (I/O redirection)
2> file, 2>> file        (stderr redirect)
2>&1                     (merge stderr into stdout)

Variable Expansion

$VAR, ${VAR}, and positional parameters $1..$9 are expanded before each line executes. $@ and $* expand to all positional parameters space-joined; $# gives their count.

The following parameter expansion forms are supported:

${VAR:-default}   value if set, else default
${VAR:=default}   set and use default if unset
${VAR:+alt}       alt if set, else empty
${VAR%pat}        remove shortest suffix matching pat
${VAR%%pat}       remove longest suffix matching pat
${VAR#pat}        remove shortest prefix matching pat
${VAR##pat}       remove longest prefix matching pat
${VAR/pat/rep}    replace first match of pat with rep
${VAR//pat/rep}   replace all matches of pat with rep
${VAR^^}          convert to uppercase
${VAR^}           uppercase first character
${VAR,,}          convert to lowercase
${VAR,}           lowercase first character
${VAR:N:L}        substring from offset N, length L
${VAR:N}          substring from offset N to end
${#VAR}           length of value

Patterns use shell glob syntax: * matches any string, ? matches any single character, [abc] matches a character class.

Function Definitions

Shell functions are defined with name() { ... } or function name { ... }. Inline single-line bodies are also supported: name() { cmd; }. Functions receive arguments as $1..$9 and $@. The caller's positional parameters are saved before the call and restored on return.

Pipeline

The | operator is supported in SH mode. The left side's standard output is written to a temporary file (File::Spec->tmpdir()), which is then fed as standard input to the right side. Multiple pipes (cmd1 | cmd2 | cmd3) are handled by chaining temporary files. All temporary files are removed after use. This implementation is Pure Perl and Perl 5.005_03 compatible.

I/O Redirection

cmd > file      stdout overwrite (create or truncate)
cmd >> file     stdout append
cmd < file      stdin from file
cmd 2> file     stderr overwrite
cmd 2>> file    stderr append
cmd 2>&1        merge stderr into stdout (current stdout target)
cmd 1>&2        merge stdout into stderr

Redirections are parsed after variable expansion, so filenames may contain variables (e.g. echo text > $outfile). All file handles use bareword globs for Perl 5.005_03 compatibility.

Compound Commands

cmd1 && cmd2    run cmd2 only if cmd1 exits with status 0
cmd1 || cmd2    run cmd2 only if cmd1 exits with non-zero status
cmd1 ; cmd2     run cmd2 unconditionally after cmd1

These are detected before variable expansion to ensure short-circuit logic works correctly. Quoting (', ") and $(...) nesting are respected when splitting.

Function Definitions

name() { body }
function name { body }
name() { cmd1; cmd2; }   # inline single-line body

Functions are registered in a package-level hash %_SH_FUNCTIONS. The caller's positional parameters ($1..$9, $*) are saved before the call and restored on return. local VAR=value saves the existing value of VAR in the function's stack frame and restores it on return.

AUTHOR

INABA Hitoshi <ina@cpan.org>

LICENSE

Same as Perl itself.