A cross-platform Windows CMD/Batch interpreter written in Go. Mirrors cmd.exe's multi-phase processing model with a recursive-descent parser and executor.
Download the latest binary for your platform from the releases page, or install from source:
go install github.com/sonroyaalmerol/go-msbatch/cmd/msbatch@latest# Run a batch file
msbatch script.bat [arg1 arg2 ...]
# Run with /C (execute command and exit)
msbatch /C "echo hello & set A=1"
# Run with /K (execute command then enter interactive mode)
msbatch /K "set MYVAR=hello"
# Interactive REPL
msbatchWhen run without arguments, msbatch starts an interactive session with:
- Tab completion for commands and file paths
- Command history (saved to
~/.msbatch_history) - Line continuation with
^(More? prompt) - Batch file execution — type
./script.batto run a batch file in the current session - CMD-style prompt (customizable via
PROMPTvariable)
C:\> echo hello
hello
C:\> set MYVAR=world
C:\> echo %MYVAR%
world
C:\> ./myscript.bat arg1 arg2On Linux/macOS, msbatch can run Windows .exe files via Wine:
# Set the Wine prefix
export MSBATCH_EXE_PREFIX=wine
# Now Windows executables work
msbatch myscript.batDrive mappings follow Wine conventions by default:
Z:\maps to/(Linux root) — access the entire Unix filesystemC:\maps todrive_c— relative path for Wine's Windows directory- Other drives map to
drive_d,drive_e, etc.
Override with MSBATCH_DRIVE_X or MSBATCH_PREFIX environment variables.
Debug complex batch projects with execution tracing:
msbatch --trace script.bat # Basic trace (commands, calls, file I/O)
msbatch --trace-verbose script.bat # Verbose (also shows SET, ERRORLEVEL)Example output for a multi-file project:
[main.bat]
2: call writer.bat
CALL writer.bat
[writer.bat]
2: echo data > shared.txt
> shared.txt
3: call reader.bat
CALL reader.bat
[reader.bat]
2: set /p val=<shared.txt
< shared.txt
4: del shared.txt
DEL shared.txt
See Trace Debugging for full details.
import (
"github.com/sonroyaalmerol/go-msbatch/pkg/executor"
"github.com/sonroyaalmerol/go-msbatch/pkg/processor"
)
// Best effort CMD.EXE compatibility
proc := processor.New(env, args, executor.New())
// Custom command set
reg := executor.NewEmpty()
reg.HandleFunc("print", func(p *processor.Processor, cmd *parser.SimpleCommand) error {
fmt.Fprintln(p.Stdout, strings.Join(cmd.Args, " "))
return nil
})
proc := processor.New(env, args, reg)
// Extend built-ins with your own commands
reg := executor.New()
reg.HandleFunc("mycommand", myHandler)
proc := processor.New(env, args, reg)go test ./... # unit + integration
go test -v ./tests/... # verbose integration outputFull documentation lives in docs/.
- Architecture & Processing Phases
- Cross-Platform Behaviour
- Trace Debugging
- Variables & Expansion
- Arithmetic — SET /A
- Control Flow — IF, GOTO, CALL, EXIT, SHIFT
- FOR Loops
- Redirection & Pipes
| Command(s) | Doc |
|---|---|
ECHO |
docs/commands/echo.md |
SET |
docs/commands/set.md |
CD / CHDIR |
docs/commands/cd.md |
TYPE, DIR, MORE |
docs/commands/type-dir-more.md |
CLS, TITLE, COLOR |
docs/commands/cls-title-color.md |
VER, PAUSE, BREAK |
docs/commands/ver-pause-break.md |
DATE, TIME |
docs/commands/date-time.md |
PATH, PROMPT, VERIFY, VOL |
docs/commands/path-prompt-verify-vol.md |
PUSHD, POPD |
docs/commands/pushd-popd.md |
MKDIR / MD, RMDIR / RD |
docs/commands/mkdir-rmdir.md |
DEL / ERASE |
docs/commands/del.md |
COPY |
docs/commands/copy.md |
MOVE, REN / RENAME |
docs/commands/move-ren.md |
MKLINK |
docs/commands/mklink.md |
START |
docs/commands/start.md |
ASSOC, FTYPE |
docs/commands/assoc-ftype.md |
FIND |
docs/commands/find.md |
SORT |
docs/commands/sort.md |
TREE |
docs/commands/tree.md |
XCOPY |
docs/commands/xcopy.md |
ROBOCOPY |
docs/commands/robocopy.md |
WHERE, HOSTNAME, WHOAMI, TIMEOUT |
docs/commands/utils.md |
| Passthrough commands | docs/commands/passthrough.md |