Skip to main content
Bun Shell makes shell scripting with JavaScript & TypeScript fun. It’s a cross-platform bash-like shell with seamless JavaScript interop. Quickstart:
index.ts

Features

  • Cross-platform: works on Windows, Linux & macOS. Instead of rimraf or cross-env’, you can use Bun Shell without installing extra dependencies. Common shell commands like ls, cd, rm are implemented natively.
  • Familiar: Bun Shell is a bash-like shell, supporting redirection, pipes, environment variables and more.
  • Globs: Glob patterns are supported natively, including **, *, {expansion}, and more.
  • Template literals: Template literals execute shell commands, allowing interpolation of variables and expressions.
  • Safety: Bun Shell escapes all strings by default, preventing shell injection attacks.
  • JavaScript interop: Use Response, ArrayBuffer, Blob, Bun.file(path) and other JavaScript objects as stdin, stdout, and stderr.
  • Shell scripting: Bun Shell can be used to run shell scripts (.bun.sh files).
  • Custom interpreter: Bun Shell is written in Rust, along with its lexer, parser, and interpreter. Bun Shell is a small programming language.

Getting started

The simplest shell command is echo. To run it, use the $ template literal tag:
By default, shell commands print to stdout. To quiet the output, call .quiet():
What if you want to access the output of the command as text? Use .text():
By default, awaiting will return stdout and stderr as Buffers.

Error handling

By default, non-zero exit codes will throw an error. This ShellError contains information about the command run.
Throwing can be disabled with .nothrow(). The result’s exitCode will need to be checked manually.
The default handling of non-zero exit codes can be configured by calling .nothrow() or .throws(boolean) on the $ function itself.

Redirection

A command’s input or output may be redirected using the typical Bash operators:
  • < redirect stdin
  • > or 1> redirect stdout
  • 2> redirect stderr
  • &> redirect both stdout and stderr
  • >> or 1>> redirect stdout, appending to the destination, instead of overwriting
  • 2>> redirect stderr, appending to the destination, instead of overwriting
  • &>> redirect both stdout and stderr, appending to the destination, instead of overwriting
  • 1>&2 redirect stdout to stderr (all writes to stdout will instead be in stderr)
  • 2>&1 redirect stderr to stdout (all writes to stderr will instead be in stdout)
Bun Shell also supports redirecting from and to JavaScript objects.

Example: Redirect output to JavaScript objects (>)

To redirect stdout to a JavaScript object, use the > operator:
The following JavaScript objects are supported for redirection to:
  • Buffer, Uint8Array, Uint16Array, Uint32Array, Int8Array, Int16Array, Int32Array, Float32Array, Float64Array, ArrayBuffer, SharedArrayBuffer (writes to the underlying buffer)
  • Bun.file(path), Bun.file(fd) (writes to the file)

Example: Redirect input from JavaScript objects (<)

To redirect the output from JavaScript objects to stdin, use the < operator:
The following JavaScript objects are supported for redirection from:
  • Buffer, Uint8Array, Uint16Array, Uint32Array, Int8Array, Int16Array, Int32Array, Float32Array, Float64Array, ArrayBuffer, SharedArrayBuffer (reads from the underlying buffer)
  • Bun.file(path), Bun.file(fd) (reads from the file)
  • Response (reads from the body)

Example: Redirect stdin -> file

Example: Redirect stdout -> file

Example: Redirect stderr -> file

Example: Redirect stderr -> stdout

Example: Redirect stdout -> stderr

Piping (|)

Like in bash, you can pipe the output of one command to another:
You can also pipe with JavaScript objects:

Command substitution ($(...))

Command substitution allows you to substitute the output of another script into the current script:
This is a textual insertion of the command’s output and can be used to, for example, declare a shell variable:
Because Bun internally uses the special raw property on the input template literal, using the backtick syntax for command substitution won’t work:
Instead of printing:
The above will print out:
We instead recommend sticking to the $(...) syntax.

Environment variables

Environment variables can be set like in bash:
You can use string interpolation to set environment variables:
Input is escaped by default, preventing shell injection attacks:

Changing the environment variables

By default, process.env is used as the environment variables for all commands. You can change the environment variables for a single command by calling .env():
You can change the default environment variables for all commands by calling $.env:
You can reset the environment variables to the default by calling $.env() with no arguments:

Changing the working directory

You can change the working directory of a command by passing a string to .cwd():
You can change the default working directory for all commands by calling $.cwd:

Reading output

To read the output of a command as a string, use .text():

Reading output as JSON

To read the output of a command as JSON, use .json():

Reading output line-by-line

To read the output of a command line-by-line, use .lines():
You can also use .lines() on a completed command:

Reading output as a Blob

To read the output of a command as a Blob, use .blob():

Builtin Commands

For cross-platform compatibility, Bun Shell implements a set of builtin commands, in addition to reading commands from the PATH environment variable.
  • cd: change the working directory
  • ls: list files in a directory (supports -l for long listing format)
  • rm: remove files and directories
  • echo: print text
  • pwd: print the working directory
  • bun: run bun in bun
  • cat
  • touch
  • mkdir
  • which
  • mv
  • exit
  • true
  • false
  • yes
  • seq
  • dirname
  • basename
Partially implemented:
  • mv: move files and directories (missing cross-device support)
Not implemented yet, but planned:

Utilities

Bun Shell also implements a set of utilities for working with shells.

$.braces (brace expansion)

This function implements simple brace expansion for shell commands:

$.escape (escape strings)

Exposes Bun Shell’s escaping logic as a function:
If you do not want your string to be escaped, wrap it in a { raw: 'str' } object:

.sh file loader

For simple shell scripts, instead of /bin/sh, you can use Bun Shell to run shell scripts. To do so, run the script with bun on a file with the .sh extension.
script.sh
terminal
Scripts with Bun Shell are cross platform, which means they work on Windows:
powershell

Implementation notes

Bun Shell is a small programming language in Bun that is implemented in Rust. It includes a handwritten lexer, parser, and interpreter. Unlike bash, zsh, and other shells, Bun Shell runs operations concurrently.

Security in the Bun shell

By design, the Bun shell does not invoke a system shell (like /bin/sh) and is instead a re-implementation of bash that runs in the same Bun process, designed with security in mind. When parsing command arguments, it treats all interpolated variables as single, literal strings. This protects the Bun shell against command injection:
In the above example, userInput is treated as a single string. This causes the ls command to try to read the contents of a single directory named “my-file; rm -rf /”.

Security considerations

While command injection is prevented by default, developers are still responsible for security in certain scenarios. Similar to the Bun.spawn or node:child_process.exec() APIs, you can intentionally execute a command which spawns a new shell (e.g. bash -c) with arguments. When you do this, you hand off control, and Bun’s built-in protections no longer apply to the string interpreted by that new shell.

Argument injection

The Bun shell cannot know how an external command interprets its own command-line arguments. An attacker can supply input that the target program recognizes as one of its own options or flags, leading to unintended behavior.
Recommendation — As is best practice in every language, always sanitize user-provided input before passing it as an argument to an external command. The responsibility for validating arguments rests with your application code.

Credits

Large parts of this API were inspired by zx, dax, and bnx. Thank you to the authors of those projects.