The Shell API provides a high-performance, cross-platform JavaScript interface for executing shell commands using template literals. It is accessed via the Bun.$ template tag and returns a ShellPromise. The implementation features a custom, non-blocking shell interpreter written in Zig and Rust that runs directly within the Bun process to minimize overhead and ensure consistency across Windows, macOS, and Linux.
The Shell API is architected in three distinct layers:
Bun.$ template function and the ShellPromise, ShellOutput, and ShellError classes in src/js/builtins/shell.ts.src/shell_parser/parse.rs (Rust), and the word expansion state machine is implemented in Rust/Zig.Title: Shell API Execution Pipeline
Sources: src/js/builtins/shell.ts1-10 src/js/builtins/shell.ts106-113 src/shell_parser/parse.rs1-55
The shell is invoked using the $ template tag, which is created by createBunShellTemplateFunction src/js/builtins/shell.ts1-2 The raw template strings are parsed by createParsedShellScript, producing a ParsedShellScript object that is then run by a ShellInterpreter src/js/builtins/shell.ts2-10
JavaScript values are automatically converted to shell arguments. Interpolated values are stored out-of-band and referenced via an internal marker byte \x08__bunstr_N in the script source, preventing injection of shell syntax test/js/bun/shell/bunshell.test.ts141-151
| Type | Conversion |
|---|---|
string | Passed as single argument (auto-escaped) |
number | Converted to string test/js/bun/shell/bunshell.test.ts110 |
boolean | Converted to "true" or "false" test/js/bun/shell/bunshell.test.ts112 |
null / undefined | Converted to "null" / "undefined" test/js/bun/shell/bunshell.test.ts113-114 |
Array | Each element becomes a separate argument test/js/bun/shell/bunshell.test.ts117 |
Buffer / Uint8Array | Used as I/O redirection target test/js/bun/shell/leak.test.ts174-175 |
Date | Converted via .toString() test/js/bun/shell/bunshell.test.ts115 |
BigInt | Converted to string test/js/bun/shell/bunshell.test.ts116 |
{ raw: string } | Injected as-is, without quoting test/js/bun/shell/bunshell.test.ts153 |
The shell supports nested template arrays up to 100 levels deep test/js/bun/shell/bunshell.test.ts119-127
Sources: test/js/bun/shell/bunshell.test.ts103-118 test/js/bun/shell/bunshell.test.ts141-158 src/js/builtins/shell.ts12-14
The $ object is the default instance of the shell. Configuration methods called directly on $ set defaults for all subsequent invocations test/js/bun/shell/bunshell.test.ts34-36
| Method | Effect |
|---|---|
$.env(obj) | Sets default environment variables src/js/builtins/shell.ts252 |
$.cwd(path) | Sets default working directory src/js/builtins/shell.ts254 |
$.nothrow() | Disables throwing on non-zero exit codes globally test/js/bun/shell/bunshell.test.ts36 |
$.escape(str) | Returns a shell-escaped version of the string test/js/bun/shell/bunshell.test.ts143 |
Sources: src/js/builtins/shell.ts252-257 test/js/bun/shell/bunshell.test.ts34-36 test/js/bun/shell/bunshell.test.ts138-182
The ShellPromise class extends Promise<ShellOutput> and manages the lifecycle of shell execution src/js/builtins/shell.ts106-108 It captures a stack trace immediately upon creation to provide meaningful error reports if the command fails src/js/builtins/shell.ts114-118
Title: Shell API Class Diagram
Sources: src/js/builtins/shell.ts16-72 src/js/builtins/shell.ts74-104 src/js/builtins/shell.ts106-250
These methods configure a single ShellPromise instance:
.cwd(path): Sets the working directory. Supports ., "", and ./ to refer to the current process CWD or the shell's defaultCwd src/js/builtins/shell.ts149-156 test/regression/issue/26460.test.ts7-29.env(object): Sets environment variables for the command src/js/builtins/shell.ts158-166.quiet(bool?): Suppresses stdout/stderr passthrough to the terminal by calling setQuiet on the internal ParsedShellScript src/js/builtins/shell.ts178-186.nothrow(): Prevents rejection on non-zero exit codes by setting #throws to false src/js/builtins/shell.ts188-191.throws(bool): Explicitly sets whether to throw on error src/js/builtins/shell.ts193-196These methods trigger execution, set the shell to quiet(true), and return a promise for the specific format:
.text(encoding?): Returns stdout as a string src/js/builtins/shell.ts198-201.json(): Parses stdout as JSON src/js/builtins/shell.ts203-206.lines(): Async generator for stdout lines, handling cross-platform \r?\n on Windows src/js/builtins/shell.ts208-216.arrayBuffer(): Returns stdout as an ArrayBuffer src/js/builtins/shell.ts218-221.blob(): Returns stdout as a Blob src/js/builtins/shell.ts227-230Bun Shell uses a custom lexer and parser to transform the template string into an Abstract Syntax Tree (AST).
The lexer (src/shell_parser/parse.rs) tokenizes the input into types like Text, Var, Semicolon, Pipe, DoubleAmpersand (&&), DoublePipe (||), and Ampersand (&) test/js/bun/shell/lex.test.ts13-24 test/js/bun/shell/lex.test.ts227-288 It handles single/double quotes and brace markers test/js/bun/shell/lex.test.ts40-51 test/js/bun/shell/lex.test.ts102-113
The parser generates a Script containing stmts (statements), which in turn contain exprs (expressions) test/js/bun/shell/parse.test.ts8-38
cmd: A simple command with assigns (env vars), name_and_args, and redirect information test/js/bun/shell/parse.test.ts13-29pipeline: Commands connected via pipes test/js/bun/shell/parse.test.ts135-154binary: Logical operators like And and Or test/js/bun/shell/parse.test.ts171-202compound: Atoms combining text and variables test/js/bun/shell/parse.test.ts108-112Sources: src/shell_parser/parse.rs59-161 test/js/bun/shell/parse.test.ts6-38 test/js/bun/shell/lex.test.ts11-134
Handled during lexing/parsing, expanding patterns like {ts,tsx,js,jsx} into multiple arguments test/js/bun/shell/lex.test.ts208-225
Bun supports standard globs (*, **, ?). Command arguments are expanded against the file system test/js/bun/shell/leak.test.ts27-38
Bun implements several commands natively (often in Rust/Zig) to ensure consistent behavior across OSs and to avoid process spawn overhead:
rm: Supports -r (recursive), -f (force), -v (verbose), and -d (remove empty directories). The implementation includes safety checks (preserve_root) and prompt behaviors src/runtime/shell/builtin/rm.rs56-84mv: Handles moving files and directories, including moving multiple files into a destination directory test/js/bun/shell/commands/mv.test.ts10-28ls: Lists directory contents, supporting recursive -R listing test/js/bun/shell/leak.test.ts25-38echo, cd, mkdir, pwd, exit, which, export, dirname, basename, cp.Sources: test/js/bun/shell/bunshell.test.ts60-81 test/js/bun/shell/commands/rm.test.ts1-147 test/js/bun/shell/commands/mv.test.ts1-52 src/runtime/shell/builtin/rm.rs16-103
When a command fails (returns a non-zero exit code), the ShellPromise rejects with a ShellError unless .nothrow() is used src/js/builtins/shell.ts124-126
The ShellError provides:
exitCode: The status code from the process src/js/builtins/shell.ts50stdout / stderr: Buffers containing the command output src/js/builtins/shell.ts48-49info: A metadata object containing exitCode, stdout, and stderr src/js/builtins/shell.ts34-43text(), json(), arrayBuffer(), bytes(), and blob() proxy to the underlying ShellOutput src/js/builtins/shell.ts53-71Sources: src/js/builtins/shell.ts16-72 test/js/bun/shell/bunshell.test.ts60-92
Refresh this wiki